This is an automated email from the ASF dual-hosted git repository.

github-actions[bot] pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git


The following commit(s) were added to refs/heads/master by this push:
     new 277dc0b3 Validation of observable metrics (#1019)
277dc0b3 is described below

commit 277dc0b3a7d16f214e5b3d2b7024b601a2e96550
Author: mfordjody <[email protected]>
AuthorDate: Sat Aug 15 14:23:32 2026 +0800

    Validation of observable metrics (#1019)
    
    * fix: refresh Inherent runtime config on Telemetry changes
    
    * feat: support required Telemetry standard metrics
    
    * fix: make CNI upgrades converge
    
    * test: validate application standard metrics end to end
    
    * feat: automate single-container application metrics
---
 cni/main.go                                        |   6 +
 cni/pkg/nodeagent/iptables.go                      |  36 +++-
 cni/pkg/nodeagent/iptables_test.go                 |   3 +
 dubbod/discovery/docker/dockerfile.dubbod          |   4 +-
 .../pkg/bootstrap/inherent_grpc_controller_test.go |  30 ++++
 dubbod/discovery/pkg/bootstrap/server.go           |   2 +
 dubbod/discovery/pkg/bootstrap/server_test.go      |  10 ++
 go.mod                                             |  14 +-
 go.sum                                             |  12 ++
 manifests/charts/base/files/crd-all.gen.yaml       |   7 +-
 manifests/charts/dubbod/files/grpc-engine.yaml     |  29 ----
 .../charts/dubbod/templates/cni-daemonset.yaml     |   3 +
 manifests/charts/dubbod/templates/cni-rbac.yaml    |   2 +-
 pkg/config/validation/validators.go                |  10 ++
 pkg/config/validation/validators_test.go           |  16 ++
 pkg/kube/inject/inherent.go                        |   4 +
 pkg/kube/inject/inherent_test.go                   | 122 ++++++--------
 pkg/kube/inject/service_test.go                    | 174 +++----------------
 pkg/kube/inject/webhook.go                         | 129 +++++---------
 samples/addons/grafana.yaml                        | 187 +++++++++++++++++++++
 tests/e2e/activationapp/go.mod                     |   3 +-
 tests/e2e/activationapp/go.sum                     |   2 +
 tests/e2e/activationapp/main.go                    |  53 ++++++
 tests/e2e/testdata/telemetry-application.yaml      |  49 ++++++
 24 files changed, 542 insertions(+), 365 deletions(-)

diff --git a/cni/main.go b/cni/main.go
index be11f788..4d0cccbb 100644
--- a/cni/main.go
+++ b/cni/main.go
@@ -121,6 +121,12 @@ func runInstall(args []string) error {
                IPSetPath:       opts.IPSetPath,
                StateDir:        opts.StateDir,
        }
+       // The cluster source reads the host kubeconfig written by Install. 
Build it
+       // only after the first installation; InstallLoop repeats this 
operation to
+       // refresh the projected service-account credentials.
+       if err := nodeagent.Install(ctx, opts); err != nil {
+               return err
+       }
        cluster := clusterSource(opts, nodeName)
        go func() {
                _ = nodeagent.ReconcileLoop(ctx,
diff --git a/cni/pkg/nodeagent/iptables.go b/cni/pkg/nodeagent/iptables.go
index 200c3885..b4b609f8 100644
--- a/cni/pkg/nodeagent/iptables.go
+++ b/cni/pkg/nodeagent/iptables.go
@@ -26,10 +26,12 @@ import (
 )
 
 const (
-       meshInboundChain = "DUBBO-GRPC-INBOUND"
-       meshPodIPSet     = "DUBBO-GRPC-INBOUND-PODS"
-       meshExcludeIPSet = "DUBBO-GRPC-INBOUND-EXCLUDE"
-       dxgateAdminPort  = 26021
+       meshInboundChain   = "DUBBO-GRPC-INBOUND"
+       meshPodIPSet       = "DUBBO-GRPC-INBOUND-PODS"
+       meshExcludeIPSet   = "DUBBO-GRPC-INBOUND-EXCLUDE"
+       legacyInboundChain = "DUBBO-XSERVER-INBOUND"
+       legacyPodIPSet     = "DUBBO-XSERVER-PODS"
+       dxgateAdminPort    = 26021
        // dxproxyAdminPort carries the inbound sidecar's health, readiness and
        // metrics endpoints. kubelet probes it from the host, so the fence has 
to
        // exempt it or every injected pod fails its readiness probe. Workloads 
that
@@ -320,11 +322,16 @@ func (m *IPTablesRuleManager) ensureBase(ctx 
context.Context) error {
        // Reject only new inbound connections. Established replies to outbound
        // control-plane traffic still target a managed Pod and must not be 
fenced.
        rejectOtherTCP := []string{"-m", "set", "--match-set", meshPodIPSet, 
"dst", "-p", "tcp", "-m", "conntrack", "--ctstate", "NEW", "-j", "REJECT"}
+       // Older releases installed an unconditional reject before the allow 
rules.
+       // Remove it during every reconciliation so upgrades converge without
+       // requiring an operator to flush the chain manually.
+       legacyRejectOtherTCP := []string{"-m", "set", "--match-set", 
meshPodIPSet, "dst", "-p", "tcp", "-j", "REJECT"}
        m.deleteRepeated(ctx, allowExcluded...)
        m.deleteRepeated(ctx, allowGRPCInbound...)
        m.deleteRepeated(ctx, allowDxgateAdmin...)
        m.deleteRepeated(ctx, allowDxproxyAdmin...)
        m.deleteRepeated(ctx, rejectOtherTCP...)
+       m.deleteRepeated(ctx, legacyRejectOtherTCP...)
        if err := m.appendRule(ctx, allowExcluded...); err != nil {
                return err
        }
@@ -337,7 +344,26 @@ func (m *IPTablesRuleManager) ensureBase(ctx 
context.Context) error {
        if err := m.appendRule(ctx, allowDxproxyAdmin...); err != nil {
                return err
        }
-       return m.appendRule(ctx, rejectOtherTCP...)
+       if err := m.appendRule(ctx, rejectOtherTCP...); err != nil {
+               return err
+       }
+       m.cleanupLegacyBase(ctx)
+       return nil
+}
+
+func (m *IPTablesRuleManager) cleanupLegacyBase(ctx context.Context) {
+       for _, chain := range []string{"FORWARD", "OUTPUT"} {
+               for i := 0; i < 20; i++ {
+                       if err := m.run(ctx, "-w", "-t", "filter", "-D", chain, 
"-j", legacyInboundChain); err != nil {
+                               break
+                       }
+               }
+       }
+       // Migration cleanup is best effort: absence means the node is already
+       // clean, while the new chain is fully installed before this runs.
+       _ = m.run(ctx, "-w", "-t", "filter", "-F", legacyInboundChain)
+       _ = m.run(ctx, "-w", "-t", "filter", "-X", legacyInboundChain)
+       _ = m.runIPSet(ctx, "destroy", legacyPodIPSet)
 }
 
 func (m *IPTablesRuleManager) deleteRepeated(ctx context.Context, args 
...string) {
diff --git a/cni/pkg/nodeagent/iptables_test.go 
b/cni/pkg/nodeagent/iptables_test.go
index db002649..f56166b7 100644
--- a/cni/pkg/nodeagent/iptables_test.go
+++ b/cni/pkg/nodeagent/iptables_test.go
@@ -41,6 +41,9 @@ func TestIPTablesRuleManagerAddsGRPCInboundBoundaryRules(t 
*testing.T) {
                "-N DUBBO-GRPC-INBOUND",
                "-I FORWARD 1 -j DUBBO-GRPC-INBOUND",
                "-I OUTPUT 1 -j DUBBO-GRPC-INBOUND",
+               "-D DUBBO-GRPC-INBOUND -m set --match-set 
DUBBO-GRPC-INBOUND-PODS dst -p tcp -j REJECT",
+               "-D OUTPUT -j DUBBO-XSERVER-INBOUND",
+               "ipset destroy DUBBO-XSERVER-PODS",
                "-A DUBBO-GRPC-INBOUND -m set --match-set 
DUBBO-GRPC-INBOUND-EXCLUDE dst,dst -p tcp -j RETURN",
                "-A DUBBO-GRPC-INBOUND -m set --match-set 
DUBBO-GRPC-INBOUND-PODS dst -p tcp --dport 15080 -j RETURN",
                "-A DUBBO-GRPC-INBOUND -m set --match-set 
DUBBO-GRPC-INBOUND-PODS dst -p tcp --dport 26021 -j RETURN",
diff --git a/dubbod/discovery/docker/dockerfile.dubbod 
b/dubbod/discovery/docker/dockerfile.dubbod
index 2bf37f69..6fa392c5 100644
--- a/dubbod/discovery/docker/dockerfile.dubbod
+++ b/dubbod/discovery/docker/dockerfile.dubbod
@@ -49,7 +49,9 @@ RUN --mount=type=cache,target=/go/pkg/mod \
     -o /out/dubbo-cni \
     ./cni/main.go
 
-FROM scratch
+FROM alpine:3.22
+
+RUN apk add --no-cache ipset iptables
 
 COPY --from=builder /out/dubbod /usr/local/bin/dubbod
 COPY --from=builder /out/dubbo-cni /usr/local/bin/dubbo-cni
diff --git a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go 
b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
index 164e6ebe..c965b697 100644
--- a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
+++ b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
@@ -251,6 +251,36 @@ func TestBuildRuntimeConfigJSON(t *testing.T) {
        }
 }
 
+func TestInherentTelemetrySerializesAllStandardMetrics(t *testing.T) {
+       metrics := []telemetryapi.StandardMetric{
+               telemetryapi.StandardMetric_REQUEST_COUNT,
+               telemetryapi.StandardMetric_REQUEST_DURATION,
+               telemetryapi.StandardMetric_REQUEST_SIZE,
+               telemetryapi.StandardMetric_RESPONSE_SIZE,
+       }
+       effective := telemetryconfig.EffectiveTracing{
+               MetricsConfigured: true,
+               MetricProviders:   []string{telemetryconfig.PrometheusProvider},
+               MetricRules:       make([]telemetryconfig.MetricRule, 0, 
len(metrics)),
+       }
+       for _, metric := range metrics {
+               effective.MetricRules = append(effective.MetricRules, 
telemetryconfig.MetricRule{
+                       Metric: metric,
+                       Scope:  telemetryapi.MetricScope_CLIENT_AND_SERVER,
+               })
+       }
+
+       got := inherentGRPCTelemetryConfig(effective)
+       if got == nil || got.Metrics == nil || len(got.Metrics.Rules) != 
len(metrics) {
+               t.Fatalf("telemetry metrics = %#v", got)
+       }
+       for i, metric := range metrics {
+               if got.Metrics.Rules[i].Metric != metric.String() {
+                       t.Fatalf("rule[%d].metric = %q, want %q", i, 
got.Metrics.Rules[i].Metric, metric)
+               }
+       }
+}
+
 func TestResolveInherentTelemetryForWorkload(t *testing.T) {
        env, _ := newInherentRuntimeTestEnvironment(t, []config.Config{{
                Meta: config.Meta{
diff --git a/dubbod/discovery/pkg/bootstrap/server.go 
b/dubbod/discovery/pkg/bootstrap/server.go
index 482e1a81..22fc5cd5 100644
--- a/dubbod/discovery/pkg/bootstrap/server.go
+++ b/dubbod/discovery/pkg/bootstrap/server.go
@@ -622,6 +622,8 @@ func configKindForSchemaIdentifier(schemaID string) 
(kind.Kind, bool) {
                return kind.DxgateService, true
        case "ServiceActivationPolicy":
                return kind.ServiceActivationPolicy, true
+       case "Telemetry":
+               return kind.Telemetry, true
        default:
                return 0, false
        }
diff --git a/dubbod/discovery/pkg/bootstrap/server_test.go 
b/dubbod/discovery/pkg/bootstrap/server_test.go
index a04ae89c..94f9aa77 100644
--- a/dubbod/discovery/pkg/bootstrap/server_test.go
+++ b/dubbod/discovery/pkg/bootstrap/server_test.go
@@ -30,3 +30,13 @@ func 
TestConfigKindForSchemaIdentifierIncludesDxgateService(t *testing.T) {
                t.Fatalf("kind = %v, want %v", got, kind.DxgateService)
        }
 }
+
+func TestConfigKindForSchemaIdentifierIncludesTelemetry(t *testing.T) {
+       got, found := configKindForSchemaIdentifier("Telemetry")
+       if !found {
+               t.Fatal("Telemetry schema identifier was not mapped")
+       }
+       if got != kind.Telemetry {
+               t.Fatalf("kind = %v, want %v", got, kind.Telemetry)
+       }
+}
diff --git a/go.mod b/go.mod
index 44e1549a..0d80c333 100644
--- a/go.mod
+++ b/go.mod
@@ -17,10 +17,6 @@ module github.com/apache/dubbo-kubernetes
 
 go 1.25.12
 
-replace github.com/kdubbo/api => ../api
-
-replace github.com/kdubbo/client-go => ../client-go
-
 require (
        k8s.io/apimachinery v0.34.1
        k8s.io/client-go v0.34.1
@@ -45,9 +41,9 @@ require (
        github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
        github.com/hashicorp/go-multierror v1.1.1
        github.com/hashicorp/golang-lru/v2 v2.0.7
-       github.com/kdubbo/api v0.0.0-20260811085311-7752d1da2bcb
-       github.com/kdubbo/client-go v0.0.0-20260809042806-04a31db14165
-       github.com/kdubbo/xds-api v0.0.0-20260809042456-0d57cc43a21a
+       github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f
+       github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3
+       github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3
        github.com/prometheus/client_golang v1.23.2
        github.com/prometheus/client_model v0.6.2
        github.com/spf13/cobra v1.10.2
@@ -146,7 +142,7 @@ require (
        golang.org/x/oauth2 v0.36.0 // indirect
        golang.org/x/sync v0.22.0 // indirect
        golang.org/x/text v0.40.0 // indirect
-       google.golang.org/genproto/googleapis/api 
v0.0.0-20260414002931-afd174a4e478 // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20260420184626-e10c466a9529 // indirect
        google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260427160629-7cedc36a6bc4 // indirect
        gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
        gopkg.in/inf.v0 v0.9.1 // indirect
@@ -158,3 +154,5 @@ require (
        sigs.k8s.io/randfill v1.0.0 // indirect
        sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
 )
+
+replace google.golang.org/genproto => google.golang.org/genproto 
v0.0.0-20260427160629-7cedc36a6bc4
diff --git a/go.sum b/go.sum
index bab6d2cf..fea71123 100644
--- a/go.sum
+++ b/go.sum
@@ -154,8 +154,16 @@ github.com/inconshreveable/mousetrap v1.1.0 
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
 github.com/inconshreveable/mousetrap v1.1.0/go.mod 
h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/json-iterator/go v1.1.12 
h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod 
h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f 
h1:T1jlRZ+ulFTUGziUnTjMvNEtiknaHQr4vQYviz4U/kQ=
+github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f/go.mod 
h1:8BtJiIovg7QCPsCxXcw3gDf922VcvYq5ihOSvj49Rq8=
+github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3 
h1:vTpsq7IAuUqKz05NoCWpDu0MY5SdjpH5NXZpj+l4I9Q=
+github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3/go.mod 
h1:wCARoHJuh9ccSRQYlGXzPyMfUMYavnRot05dKh1N4KY=
 github.com/kdubbo/xds-api v0.0.0-20260809042456-0d57cc43a21a 
h1:stTvOOGy4r6DoxquO928mB9AGGoFBvjVHxqAGIGiSkI=
 github.com/kdubbo/xds-api v0.0.0-20260809042456-0d57cc43a21a/go.mod 
h1:o2HDUgL1ntaDbWomZ4cD2tt8jBamuG2qRtjXOa1zZ0Q=
+github.com/kdubbo/xds-api v0.0.0-20260814141925-82c6d4ca43c1 
h1:/PaNI7QypbLp32sqRd54iu1DMs2XmxHKAaVSjhcLF/4=
+github.com/kdubbo/xds-api v0.0.0-20260814141925-82c6d4ca43c1/go.mod 
h1:o2HDUgL1ntaDbWomZ4cD2tt8jBamuG2qRtjXOa1zZ0Q=
+github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3 
h1:ypir1ZNYdAKOuWokpObAdUtCDXLD3yqP1AOqCURe3WU=
+github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3/go.mod 
h1:o2HDUgL1ntaDbWomZ4cD2tt8jBamuG2qRtjXOa1zZ0Q=
 github.com/kisielk/errcheck v1.5.0/go.mod 
h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod 
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.18.6 
h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
@@ -360,8 +368,12 @@ google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod 
h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod 
h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod 
h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20260427160629-7cedc36a6bc4 
h1:2iMJZntwvmfgtse+s744JY7v7PgEdSBuFYXucvpOHNM=
+google.golang.org/genproto v0.0.0-20260427160629-7cedc36a6bc4/go.mod 
h1:v14kaaboYyXQ1Gsu489Q+Hg/oN4B33mWtuOhF1HCeXA=
 google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 
h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
 google.golang.org/genproto/googleapis/api 
v0.0.0-20260414002931-afd174a4e478/go.mod 
h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 
h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20260420184626-e10c466a9529/go.mod 
h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 
h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM=
 google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260427160629-7cedc36a6bc4/go.mod 
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
diff --git a/manifests/charts/base/files/crd-all.gen.yaml 
b/manifests/charts/base/files/crd-all.gen.yaml
index 6ca35631..95713abb 100644
--- a/manifests/charts/base/files/crd-all.gen.yaml
+++ b/manifests/charts/base/files/crd-all.gen.yaml
@@ -1626,10 +1626,13 @@ spec:
                             description: |-
                               REQUIRED.
 
-                              Valid Options: REQUEST_COUNT
+                              Valid Options: REQUEST_COUNT, REQUEST_DURATION, 
REQUEST_SIZE, RESPONSE_SIZE
                             enum:
                             - STANDARD_METRIC_UNSPECIFIED
                             - REQUEST_COUNT
+                            - REQUEST_DURATION
+                            - REQUEST_SIZE
+                            - RESPONSE_SIZE
                             type: string
                           scope:
                             description: |-
@@ -1655,7 +1658,7 @@ spec:
                                   - REMOVE
                                   type: string
                               type: object
-                            description: Tag overrides keyed by metric tag 
name.
+                            description: Tag overrides keyed by standard label 
name.
                             type: object
                         type: object
                       type: array
diff --git a/manifests/charts/dubbod/files/grpc-engine.yaml 
b/manifests/charts/dubbod/files/grpc-engine.yaml
index 36922e31..57606c0f 100644
--- a/manifests/charts/dubbod/files/grpc-engine.yaml
+++ b/manifests/charts/dubbod/files/grpc-engine.yaml
@@ -103,35 +103,6 @@ spec:
     - name: dubbo-xds
       mountPath: /etc/dubbo/proxy
       readOnly: true
-{{- $appPort := 80 }}
-{{- if and (gt (len .Spec.Containers) 0) (gt (len ((index .Spec.Containers 
0).Ports)) 0) }}
-{{- $appPort = int ((index ((index .Spec.Containers 0).Ports) 
0).ContainerPort) }}
-{{- end }}
-  - name: dubbo-grpc-inbound
-    image: {{ .ProxyImage | quote }}
-    imagePullPolicy: IfNotPresent
-    args:
-    - grpc-inbound
-    - --listen
-    - :15080
-    - --upstream
-    - 127.0.0.1:{{ $appPort }}
-    ports:
-    - name: grpc-inbound
-      containerPort: 15080
-      protocol: TCP
-    # Probe the listener the sidecar actually owns. periodSeconds x
-    # failureThreshold must stay below the sidecar's termination drain delay
-    # (5s by default), so kubelet withdraws the endpoint promptly.
-    readinessProbe:
-      tcpSocket:
-        port: 15080
-      periodSeconds: 2
-      failureThreshold: 2
-    volumeMounts:
-    - name: dubbo-xds
-      mountPath: /etc/dubbo/proxy
-      readOnly: true
   volumes:
   - name: dubbo-xds
     secret:
diff --git a/manifests/charts/dubbod/templates/cni-daemonset.yaml 
b/manifests/charts/dubbod/templates/cni-daemonset.yaml
index 15dcbb85..6ca76ccc 100644
--- a/manifests/charts/dubbod/templates/cni-daemonset.yaml
+++ b/manifests/charts/dubbod/templates/cni-daemonset.yaml
@@ -81,6 +81,9 @@ spec:
             capabilities:
               drop:
                 - ALL
+              add:
+                - NET_ADMIN
+                - NET_RAW
           volumeMounts:
             - name: cni-bin-dir
               mountPath: /opt/cni/bin
diff --git a/manifests/charts/dubbod/templates/cni-rbac.yaml 
b/manifests/charts/dubbod/templates/cni-rbac.yaml
index d51e7d2e..5e6291c9 100644
--- a/manifests/charts/dubbod/templates/cni-rbac.yaml
+++ b/manifests/charts/dubbod/templates/cni-rbac.yaml
@@ -26,7 +26,7 @@ metadata:
 rules:
   - apiGroups: [""]
     resources: ["pods"]
-    verbs: ["get"]
+    verbs: ["get", "list"]
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
diff --git a/pkg/config/validation/validators.go 
b/pkg/config/validation/validators.go
index ce82afb2..750bde73 100644
--- a/pkg/config/validation/validators.go
+++ b/pkg/config/validation/validators.go
@@ -574,6 +574,13 @@ func validateWorkloadAddress(field, value string) error {
        return validateServiceEntryHost(field, value)
 }
 
+var telemetryStandardLabels = map[string]struct{}{
+       "reporter":             {},
+       "grpc_service":         {},
+       "grpc_method":          {},
+       "grpc_response_status": {},
+}
+
 // ValidateTelemetry checks that a Telemetry resource is well-formed.
 var ValidateTelemetry = RegisterValidateFunc("ValidateTelemetry",
        func(cfg config.Config) (Warning, error) {
@@ -627,6 +634,9 @@ var ValidateTelemetry = 
RegisterValidateFunc("ValidateTelemetry",
                                for name, override := range rule.GetTags() {
                                        if strings.TrimSpace(name) == "" {
                                                v = appendValidation(v, 
fmt.Errorf("metrics[%d].rules[%d].tags contains an empty name", i, j))
+                                       } else if _, found := 
telemetryStandardLabels[name]; !found {
+                                               v = appendValidation(v, 
fmt.Errorf(
+                                                       
"metrics[%d].rules[%d].tags[%q] is not a standard label", i, j, name))
                                        }
                                        if override == nil {
                                                v = appendValidation(v, 
fmt.Errorf("metrics[%d].rules[%d].tags[%q] must not be null", i, j, name))
diff --git a/pkg/config/validation/validators_test.go 
b/pkg/config/validation/validators_test.go
index 30212f26..e71b6ee9 100644
--- a/pkg/config/validation/validators_test.go
+++ b/pkg/config/validation/validators_test.go
@@ -626,6 +626,9 @@ func TestValidateTelemetry(t *testing.T) {
                                        Metric: 
telemetry.StandardMetric_REQUEST_COUNT,
                                        Scope:  
telemetry.MetricScope_CLIENT_AND_SERVER,
                                        Tags: map[string]*telemetry.TagOverride{
+                                               "reporter":             
{Action: telemetry.TagOverride_REMOVE},
+                                               "grpc_service":         
{Action: telemetry.TagOverride_REMOVE},
+                                               "grpc_method":          
{Action: telemetry.TagOverride_REMOVE},
                                                "grpc_response_status": 
{Action: telemetry.TagOverride_REMOVE},
                                        },
                                }},
@@ -639,6 +642,19 @@ func TestValidateTelemetry(t *testing.T) {
                        }}},
                        wantErr: true,
                },
+               {
+                       name: "unknown metrics standard label",
+                       spec: &telemetry.Telemetry{Metrics: 
[]*telemetry.Metrics{{
+                               Rules: []*telemetry.MetricRule{{
+                                       Metric: 
telemetry.StandardMetric_REQUEST_COUNT,
+                                       Scope:  telemetry.MetricScope_CLIENT,
+                                       Tags: map[string]*telemetry.TagOverride{
+                                               "pod": {Action: 
telemetry.TagOverride_REMOVE},
+                                       },
+                               }},
+                       }}},
+                       wantErr: true,
+               },
                {
                        name: "metrics rule without scope",
                        spec: &telemetry.Telemetry{Metrics: 
[]*telemetry.Metrics{{
diff --git a/pkg/kube/inject/inherent.go b/pkg/kube/inject/inherent.go
index 226a170a..c8315c50 100644
--- a/pkg/kube/inject/inherent.go
+++ b/pkg/kube/inject/inherent.go
@@ -36,6 +36,10 @@ const (
        InherentGRPCBootstrapPath                   = InherentXDSMountPath + 
"/" + InherentGRPCBootstrapFileName
        InherentXDSAddressEnvName                   = "XDS_ADDRESS"
        InherentGRPCConfigEnvName                   = "DUBBO_GRPC_XDS_CONFIG"
+       InherentGRPCMetricsAddressEnvName           = 
"DUBBO_GRPC_METRICS_ADDRESS"
+       InherentGRPCMetricsAddress                  = ":9090"
+       InherentGRPCMetricsPortName                 = "metrics"
+       InherentGRPCMetricsPort                     = 9090
        InherentGRPCKeepaliveEnvName                = "DUBBO_GRPC_KEEPALIVE"
        InherentGRPCKeepaliveTimeEnv                = "GRPC_KEEPALIVE_INTERVAL"
        InherentGRPCKeepaliveTimeoutEnv             = "GRPC_KEEPALIVE_TIMEOUT"
diff --git a/pkg/kube/inject/inherent_test.go b/pkg/kube/inject/inherent_test.go
index 2dc08fc6..d4a68eb2 100644
--- a/pkg/kube/inject/inherent_test.go
+++ b/pkg/kube/inject/inherent_test.go
@@ -21,7 +21,6 @@ import (
        "runtime"
        "strings"
        "testing"
-       "time"
 
        telemetryconfig 
"github.com/apache/dubbo-kubernetes/pkg/config/telemetry"
        meshv1alpha1 "github.com/kdubbo/api/mesh/v1alpha1"
@@ -85,18 +84,17 @@ func 
TestInstallerGRPCEngineTemplateInjectsDirectXDSConnection(t *testing.T) {
                t.Fatalf("RunTemplate() failed: %v", err)
        }
 
-       if len(injectedPod.Spec.Containers) != 2 {
-               t.Fatalf("template containers = %d, want app overlay plus 
grpc-inbound", len(injectedPod.Spec.Containers))
+       if len(injectedPod.Spec.Containers) != 1 {
+               t.Fatalf("template containers = %d, want application overlay 
only", len(injectedPod.Spec.Containers))
        }
        if err := postProcessPod(mergedPod, *injectedPod, req); err != nil {
                t.Fatalf("postProcessPod() failed: %v", err)
        }
 
-       if len(mergedPod.Spec.Containers) != 2 {
-               t.Fatalf("containers = %d, want application container plus 
grpc-inbound", len(mergedPod.Spec.Containers))
+       if len(mergedPod.Spec.Containers) != 1 {
+               t.Fatalf("containers = %d, want original application container 
only", len(mergedPod.Spec.Containers))
        }
        assertDirectXDSConnection(t, mergedPod, "app", 
InherentGRPCSecretNameForMeta(pod.ObjectMeta))
-       assertGRPCInboundContainer(t, mergedPod)
 }
 
 func TestInstallerGRPCEngineTemplateUsesGenerateNameForDeploymentPods(t 
*testing.T) {
@@ -155,11 +153,10 @@ func 
TestInstallerGRPCEngineTemplateUsesGenerateNameForDeploymentPods(t *testing
        if err := postProcessPod(mergedPod, *injectedPod, req); err != nil {
                t.Fatalf("postProcessPod() failed: %v", err)
        }
-       if len(mergedPod.Spec.Containers) != 2 {
-               t.Fatalf("containers = %d, want original nginx container plus 
grpc-inbound", len(mergedPod.Spec.Containers))
+       if len(mergedPod.Spec.Containers) != 1 {
+               t.Fatalf("containers = %d, want original application container 
only", len(mergedPod.Spec.Containers))
        }
        assertDirectXDSConnection(t, mergedPod, "nginx", 
InherentGRPCSecretNameForMeta(pod.ObjectMeta))
-       assertGRPCInboundContainer(t, mergedPod)
        if got := mergedPod.Spec.Volumes[0].Secret.SecretName; got == 
InherentGRPCSecretName("") {
                t.Fatalf("secret name = %q, want generateName-based secret", 
got)
        }
@@ -178,6 +175,21 @@ func assertDirectXDSConnection(t *testing.T, pod 
*corev1.Pod, containerName, sec
        if !hasEnv(container.Env, InherentGRPCConfigEnvName, 
InherentGRPCConfigPath) {
                t.Fatalf("%s env missing", InherentGRPCConfigEnvName)
        }
+       if !hasEnv(container.Env, InherentGRPCMetricsAddressEnvName, 
InherentGRPCMetricsAddress) {
+               t.Fatalf("%s env missing", InherentGRPCMetricsAddressEnvName)
+       }
+       foundMetricsPort := false
+       for _, port := range container.Ports {
+               if port.Name == InherentGRPCMetricsPortName && 
port.ContainerPort == InherentGRPCMetricsPort {
+                       foundMetricsPort = true
+               }
+       }
+       if !foundMetricsPort {
+               t.Fatalf("metrics port %d missing", InherentGRPCMetricsPort)
+       }
+       if got := pod.Annotations["prometheus.io/scrape"]; got != "true" {
+               t.Fatalf("prometheus scrape annotation = %q, want true", got)
+       }
        if !hasEnv(container.Env, InherentXDSAddressEnvName, 
"dubbod.dubbo-system.svc:26012") {
                t.Fatalf("%s env missing", InherentXDSAddressEnvName)
        }
@@ -244,53 +256,6 @@ func assertNoArgs(t *testing.T, pod *corev1.Pod) {
        }
 }
 
-// inherentDrainDelay mirrors the sidecar's default termination drain delay.
-// The readiness probe must detect termination inside this window.
-const inherentDrainDelay = 5 * time.Second
-
-func assertGRPCInboundContainer(t *testing.T, pod *corev1.Pod) {
-       t.Helper()
-       container := FindContainer(InherentGRPCInboundContainerName, 
pod.Spec.Containers)
-       if container == nil {
-               t.Fatalf("%s container missing", 
InherentGRPCInboundContainerName)
-       }
-       if container.Image != "kdubbo/dubbod:debug" {
-               t.Fatalf("grpc-inbound image = %q, want kdubbo/dubbod:debug", 
container.Image)
-       }
-       wantArgs := []string{"grpc-inbound", "--listen", ":15080", 
"--upstream", "127.0.0.1:80"}
-       if strings.Join(container.Args, ",") != strings.Join(wantArgs, ",") {
-               t.Fatalf("grpc-inbound args = %v, want %v", container.Args, 
wantArgs)
-       }
-       if !hasMount(container.VolumeMounts, InherentXDSVolumeName, 
InherentXDSMountPath, true) {
-               t.Fatalf("grpc-inbound inherent xds mount missing")
-       }
-       assertDrainReadinessProbe(t, container)
-}
-
-// assertDrainReadinessProbe checks the probe that withdraws a terminating pod
-// from its EndpointSlice. Without it the sidecar's drain delay is inert: 
kubelet
-// never observes the listener closing, so the endpoint is still published
-// after the data-plane port is gone.
-func assertDrainReadinessProbe(t *testing.T, container *corev1.Container) {
-       t.Helper()
-       probe := container.ReadinessProbe
-       if probe == nil || probe.TCPSocket == nil {
-               t.Fatalf("grpc-inbound readiness probe missing")
-       }
-       if probe.TCPSocket.Port.IntValue() != InherentGRPCInboundPort {
-               t.Fatalf("grpc-inbound readiness probe = %v, want TCP port %d",
-                       probe.TCPSocket.Port, InherentGRPCInboundPort)
-       }
-       // The probe has to fail before the sidecar stops accepting, otherwise 
the
-       // endpoint is withdrawn only after the listener is already gone.
-       if detection := 
time.Duration(probe.PeriodSeconds*probe.FailureThreshold) * time.Second; 
detection >= inherentDrainDelay {
-               t.Fatalf("readiness detection window = %v, want less than the 
%v drain delay", detection, inherentDrainDelay)
-       }
-       if !hasContainerPort(container.Ports, InherentGRPCInboundPort) {
-               t.Fatalf("grpc-inbound port %d not declared", 
InherentGRPCInboundPort)
-       }
-}
-
 func TestGetProxyImageUsesTopLevelImage(t *testing.T) {
        values := map[string]any{
                "image": "kdubbo/dubbod:test",
@@ -300,15 +265,6 @@ func TestGetProxyImageUsesTopLevelImage(t *testing.T) {
        }
 }
 
-func hasContainerPort(ports []corev1.ContainerPort, want int) bool {
-       for _, port := range ports {
-               if int(port.ContainerPort) == want {
-                       return true
-               }
-       }
-       return false
-}
-
 func TestAddApplicationContainerConfigInjectsInherentGRPCContract(t 
*testing.T) {
        pod := &corev1.Pod{
                ObjectMeta: metav1.ObjectMeta{
@@ -586,14 +542,6 @@ func TestEnsureInherentGRPCTemplateAnnotation(t 
*testing.T) {
        }
 }
 
-func TestEnsureInherentManagedLabel(t *testing.T) {
-       pod := &corev1.Pod{}
-       ensureInherentManagedLabel(pod)
-       if got := pod.Labels[InherentManagedLabel]; got != 
InherentManagedLabelValue {
-               t.Fatalf("managed label = %q, want %q", got, 
InherentManagedLabelValue)
-       }
-}
-
 func TestInherentGRPCSecretNameFitsKubernetesLengthLimit(t *testing.T) {
        name := 
InherentGRPCSecretName("grpc-provider-012345678901234567890123456789012345678901234567890123")
        if len(name) > 63 {
@@ -712,6 +660,34 @@ func TestInstallerGRPCEngineTemplateInjectsTelemetryEnv(t 
*testing.T) {
        if err != nil {
                t.Fatalf("RunTemplate() failed: %v", err)
        }
+       if err := postProcessPod(mergedPod, corev1.Pod{}, newParams(tracing)); 
err != nil {
+               t.Fatalf("postProcessPod() failed: %v", err)
+       }
+       if got := envValue(mergedPod, InherentGRPCMetricsAddressEnvName); got 
!= InherentGRPCMetricsAddress {
+               t.Fatalf("%s = %q, want %q", InherentGRPCMetricsAddressEnvName, 
got, InherentGRPCMetricsAddress)
+       }
+       app := FindContainer("app", mergedPod.Spec.Containers)
+       if app == nil {
+               t.Fatal("app container not found")
+       }
+       foundMetricsPort := false
+       for _, port := range app.Ports {
+               if port.Name == InherentGRPCMetricsPortName && 
port.ContainerPort == InherentGRPCMetricsPort {
+                       foundMetricsPort = true
+               }
+       }
+       if !foundMetricsPort {
+               t.Fatalf("application metrics port %d missing", 
InherentGRPCMetricsPort)
+       }
+       for key, want := range map[string]string{
+               "prometheus.io/scrape": "true",
+               "prometheus.io/path":   "/metrics",
+               "prometheus.io/port":   "9090",
+       } {
+               if got := mergedPod.Annotations[key]; got != want {
+                       t.Fatalf("annotation %s = %q, want %q", key, got, want)
+               }
+       }
        if got, want := envValue(mergedPod, "OTEL_EXPORTER_OTLP_ENDPOINT"), 
"http://tracing.dubbo-system.svc:4317";; got != want {
                t.Fatalf("OTEL_EXPORTER_OTLP_ENDPOINT = %q, want %q", got, want)
        }
diff --git a/pkg/kube/inject/service_test.go b/pkg/kube/inject/service_test.go
index d5546a4e..a8a58eee 100644
--- a/pkg/kube/inject/service_test.go
+++ b/pkg/kube/inject/service_test.go
@@ -1,23 +1,12 @@
 // Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
+// contributor license agreements. See the NOTICE file distributed with
 // this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// The ASF licenses this file to You under the Apache License, Version 2.0.
 
 package inject
 
 import (
        "encoding/json"
-       "strings"
        "testing"
 
        "github.com/apache/dubbo-kubernetes/pkg/kube"
@@ -28,105 +17,24 @@ import (
        "k8s.io/apimachinery/pkg/util/intstr"
 )
 
-func TestRewriteInherentServiceTargetPortsRoutesTCPServiceToGRPCInbound(t 
*testing.T) {
-       svc := &corev1.Service{
-               ObjectMeta: metav1.ObjectMeta{Name: "nginx", Namespace: "app"},
+func TestInjectServiceDoesNotRewriteApplicationTargetPort(t *testing.T) {
+       service := corev1.Service{
+               ObjectMeta: metav1.ObjectMeta{Name: "payment", Namespace: 
"app"},
                Spec: corev1.ServiceSpec{
-                       Selector: map[string]string{"app": "nginx"},
-                       Ports: []corev1.ServicePort{
-                               {Name: "http", Port: 80, TargetPort: 
intstr.FromInt(80), Protocol: corev1.ProtocolTCP},
-                               {Name: "metrics", Port: 9090, TargetPort: 
intstr.FromString("metrics"), Protocol: corev1.ProtocolTCP},
-                       },
+                       Selector: map[string]string{"app": "payment"},
+                       Ports: []corev1.ServicePort{{
+                               Name:       "grpc",
+                               Port:       80,
+                               TargetPort: intstr.FromInt(8080),
+                               Protocol:   corev1.ProtocolTCP,
+                       }},
                },
        }
-
-       if !rewriteInherentServiceTargetPorts(svc) {
-               t.Fatalf("rewriteInherentServiceTargetPorts() = false, want 
true")
-       }
-       for _, port := range svc.Spec.Ports {
-               if got := port.TargetPort.IntVal; got != 
InherentGRPCInboundPort {
-                       t.Fatalf("port %s targetPort = %d, want %d", port.Name, 
got, InherentGRPCInboundPort)
-               }
-       }
-}
-
-func TestRewriteInherentServiceTargetPortsSkipsUnsafeServices(t *testing.T) {
-       cases := []struct {
-               name string
-               svc  corev1.Service
-       }{
-               {
-                       name: "selectorless",
-                       svc: corev1.Service{
-                               ObjectMeta: metav1.ObjectMeta{Name: "manual", 
Namespace: "app"},
-                               Spec: corev1.ServiceSpec{
-                                       Ports: []corev1.ServicePort{{Name: 
"http", Port: 80, TargetPort: intstr.FromInt(80)}},
-                               },
-                       },
-               },
-               {
-                       name: "external-name",
-                       svc: corev1.Service{
-                               ObjectMeta: metav1.ObjectMeta{Name: "external", 
Namespace: "app"},
-                               Spec: corev1.ServiceSpec{
-                                       Type:         
corev1.ServiceTypeExternalName,
-                                       ExternalName: "example.com",
-                                       Selector:     map[string]string{"app": 
"external"},
-                                       Ports:        
[]corev1.ServicePort{{Name: "http", Port: 80, TargetPort: intstr.FromInt(80)}},
-                               },
-                       },
-               },
-       }
-
-       for _, tt := range cases {
-               t.Run(tt.name, func(t *testing.T) {
-                       svc := tt.svc
-                       if rewriteInherentServiceTargetPorts(&svc) {
-                               t.Fatalf("rewriteInherentServiceTargetPorts() = 
true, want false")
-                       }
-                       if got := svc.Spec.Ports[0].TargetPort.IntVal; got != 
80 {
-                               t.Fatalf("targetPort = %d, want 80", got)
-                       }
-               })
-       }
-}
-
-func TestRewriteInherentServiceTargetPortsSkipsNonTCPPorts(t *testing.T) {
-       svc := &corev1.Service{
-               ObjectMeta: metav1.ObjectMeta{Name: "dns", Namespace: "app"},
-               Spec: corev1.ServiceSpec{
-                       Selector: map[string]string{"app": "dns"},
-                       Ports: []corev1.ServicePort{
-                               {Name: "dns", Port: 53, TargetPort: 
intstr.FromInt(53), Protocol: corev1.ProtocolUDP},
-                       },
-               },
-       }
-
-       if rewriteInherentServiceTargetPorts(svc) {
-               t.Fatalf("rewriteInherentServiceTargetPorts() = true, want 
false")
-       }
-       if got := svc.Spec.Ports[0].TargetPort.IntVal; got != 53 {
-               t.Fatalf("targetPort = %d, want 53", got)
-       }
-}
-
-func TestInjectServiceCreatesGRPCInboundTargetPortPatch(t *testing.T) {
-       svc := corev1.Service{
-               ObjectMeta: metav1.ObjectMeta{Name: "nginx", Namespace: "app"},
-               Spec: corev1.ServiceSpec{
-                       Selector: map[string]string{"app": "nginx"},
-                       Ports: []corev1.ServicePort{
-                               {Name: "http", Port: 80, TargetPort: 
intstr.FromInt(80), Protocol: corev1.ProtocolTCP},
-                       },
-               },
-       }
-       raw, err := json.Marshal(svc)
+       raw, err := json.Marshal(service)
        if err != nil {
-               t.Fatalf("json.Marshal() failed: %v", err)
+               t.Fatal(err)
        }
-
-       wh := &Webhook{Config: &Config{Policy: InjectionPolicyEnabled}}
-       resp := wh.injectService(&kube.AdmissionReview{
+       response := (&Webhook{}).injectService(&kube.AdmissionReview{
                Request: &kube.AdmissionRequest{
                        UID:       types.UID("test"),
                        Kind:      metav1.GroupVersionKind{Version: "v1", Kind: 
"Service"},
@@ -135,54 +43,10 @@ func TestInjectServiceCreatesGRPCInboundTargetPortPatch(t 
*testing.T) {
                        Object:    runtime.RawExtension{Raw: raw},
                },
        }, "/inject")
-
-       if !resp.Allowed {
-               t.Fatalf("Allowed = false, want true")
-       }
-       if resp.PatchType == nil || *resp.PatchType != "JSONPatch" {
-               t.Fatalf("PatchType = %v, want JSONPatch", resp.PatchType)
-       }
-       if !strings.Contains(string(resp.Patch), `"value":15080`) {
-               t.Fatalf("patch = %s, want targetPort 15080", 
string(resp.Patch))
-       }
-}
-
-func TestInjectServiceSkipsInherentOptOutService(t *testing.T) {
-       svc := corev1.Service{
-               ObjectMeta: metav1.ObjectMeta{
-                       Name:      "dxgate-gateway",
-                       Namespace: "app",
-                       Labels: map[string]string{
-                               "inherent.dubbo.apache.org/inject": "false",
-                       },
-               },
-               Spec: corev1.ServiceSpec{
-                       Selector: map[string]string{"app.kubernetes.io/name": 
"dxgate"},
-                       Ports: []corev1.ServicePort{
-                               {Name: "http", Port: 80, TargetPort: 
intstr.FromString("http"), Protocol: corev1.ProtocolTCP},
-                       },
-               },
-       }
-       raw, err := json.Marshal(svc)
-       if err != nil {
-               t.Fatalf("json.Marshal() failed: %v", err)
-       }
-
-       wh := &Webhook{Config: &Config{Policy: InjectionPolicyEnabled}}
-       resp := wh.injectService(&kube.AdmissionReview{
-               Request: &kube.AdmissionRequest{
-                       UID:       types.UID("test"),
-                       Kind:      metav1.GroupVersionKind{Version: "v1", Kind: 
"Service"},
-                       Namespace: "app",
-                       Operation: kube.Create,
-                       Object:    runtime.RawExtension{Raw: raw},
-               },
-       }, "/inject")
-
-       if !resp.Allowed {
-               t.Fatalf("Allowed = false, want true")
+       if !response.Allowed {
+               t.Fatal("service admission rejected")
        }
-       if len(resp.Patch) != 0 {
-               t.Fatalf("patch = %s, want no patch", string(resp.Patch))
+       if len(response.Patch) != 0 {
+               t.Fatalf("service patch = %s, want no targetPort rewrite", 
response.Patch)
        }
 }
diff --git a/pkg/kube/inject/webhook.go b/pkg/kube/inject/webhook.go
index fc4257d4..508ece0b 100644
--- a/pkg/kube/inject/webhook.go
+++ b/pkg/kube/inject/webhook.go
@@ -23,6 +23,7 @@ import (
 
        "net/http"
        "os"
+       "strconv"
        "strings"
        "sync"
        "text/template"
@@ -47,7 +48,6 @@ import (
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/runtime/serializer"
        "k8s.io/apimachinery/pkg/types"
-       "k8s.io/apimachinery/pkg/util/intstr"
        "k8s.io/apimachinery/pkg/util/mergepatch"
        "k8s.io/apimachinery/pkg/util/strategicpatch"
        "sigs.k8s.io/yaml"
@@ -323,52 +323,7 @@ func (wh *Webhook) injectPod(ar *kube.AdmissionReview, 
path string) *kube.Admiss
 }
 
 func (wh *Webhook) injectService(ar *kube.AdmissionReview, path string) 
*kube.AdmissionResponse {
-       log := webhookLog.WithLabels("path", path)
-       req := ar.Request
-       var svc corev1.Service
-       if err := json.Unmarshal(req.Object.Raw, &svc); err != nil {
-               log.Errorf("Could not unmarshal raw service object: %v %s", 
err, string(req.Object.Raw))
-               return toAdmissionResponse(err)
-       }
-       if svc.Namespace == "" {
-               svc.Namespace = req.Namespace
-       }
-
-       log = log.WithLabels("service", svc.Namespace+"/"+svc.Name)
-       log.Infof("Process inherent service request")
-
-       wh.mu.RLock()
-       required := injectRequired(IgnoredNamespaces.UnsortedList(), wh.Config, 
&corev1.PodSpec{}, svc.ObjectMeta)
-       wh.mu.RUnlock()
-       if !required {
-               log.Infof("Skipping service due to policy check")
-               return &kube.AdmissionResponse{Allowed: true}
-       }
-
-       originalService, err := json.Marshal(svc)
-       if err != nil {
-               return toAdmissionResponse(err)
-       }
-       if !rewriteInherentServiceTargetPorts(&svc) {
-               return &kube.AdmissionResponse{Allowed: true}
-       }
-
-       patchBytes, err := createServicePatch(&svc, originalService)
-       if err != nil {
-               log.Errorf("Service injection failed: %v", err)
-               return toAdmissionResponse(err)
-       }
-
-       log.Infof("Service injection successfully, patch size: %d bytes", 
len(patchBytes))
-       reviewResponse := kube.AdmissionResponse{
-               Allowed: true,
-               Patch:   patchBytes,
-               PatchType: func() *string {
-                       pt := "JSONPatch"
-                       return &pt
-               }(),
-       }
-       return &reviewResponse
+       return &kube.AdmissionResponse{Allowed: true}
 }
 
 func (wh *Webhook) Run(stop <-chan struct{}) {
@@ -454,12 +409,9 @@ func postProcessPod(pod *corev1.Pod, injectedPod 
corev1.Pod, req InjectionParame
        if shouldInjectInherentGRPC(req) {
                // Add Inherent gRPC env and shared bootstrap/cert volume to 
application containers.
                ensureInherentGRPCTemplateAnnotation(pod)
-               ensureInherentManagedLabel(pod)
                if err := addApplicationContainerConfig(pod, req); err != nil {
                        return err
                }
-               // Must run after the sidecar is merged in: the rewrite reads 
the port it forwards to.
-               RewriteAppProbes(pod)
        }
 
        if err := reorderPod(pod, req); err != nil {
@@ -543,6 +495,19 @@ func addApplicationContainerConfig(pod *corev1.Pod, req 
InjectionParameters) err
                pod.Spec.Volumes = append(pod.Spec.Volumes, desiredVolume)
        }
 
+       if len(pod.Spec.Containers) > 0 {
+               application := &pod.Spec.Containers[0]
+               application.Env = ensureEnvVar(application.Env, corev1.EnvVar{
+                       Name:  InherentGRPCMetricsAddressEnvName,
+                       Value: InherentGRPCMetricsAddress,
+               })
+               application.Ports = ensureContainerPort(application.Ports, 
corev1.ContainerPort{
+                       Name:          InherentGRPCMetricsPortName,
+                       ContainerPort: InherentGRPCMetricsPort,
+                       Protocol:      corev1.ProtocolTCP,
+               })
+               ensureApplicationMetricsAnnotations(pod)
+       }
        for i := range pod.Spec.Containers {
                container := &pod.Spec.Containers[i]
                if container.Name == "dubbo-proxy" || container.Name == 
"dubbo-validation" {
@@ -668,6 +633,30 @@ func addApplicationContainerConfig(pod *corev1.Pod, req 
InjectionParameters) err
        return nil
 }
 
+func ensureContainerPort(ports []corev1.ContainerPort, desired 
corev1.ContainerPort) []corev1.ContainerPort {
+       for _, port := range ports {
+               if port.Name == desired.Name || port.ContainerPort == 
desired.ContainerPort {
+                       return ports
+               }
+       }
+       return append(ports, desired)
+}
+
+func ensureApplicationMetricsAnnotations(pod *corev1.Pod) {
+       if pod.Annotations == nil {
+               pod.Annotations = map[string]string{}
+       }
+       for key, value := range map[string]string{
+               "prometheus.io/scrape": "true",
+               "prometheus.io/path":   "/metrics",
+               "prometheus.io/port":   strconv.Itoa(InherentGRPCMetricsPort),
+       } {
+               if _, found := pod.Annotations[key]; !found {
+                       pod.Annotations[key] = value
+               }
+       }
+}
+
 func ensureInherentGRPCTemplateAnnotation(pod *corev1.Pod) {
        if pod.Annotations == nil {
                pod.Annotations = map[string]string{}
@@ -685,13 +674,6 @@ func ensureInherentGRPCTemplateAnnotation(pod *corev1.Pod) 
{
        pod.Annotations[InherentInjectTemplatesAnnoName] = templates + "," + 
InherentGRPCTemplateName
 }
 
-func ensureInherentManagedLabel(pod *corev1.Pod) {
-       if pod.Labels == nil {
-               pod.Labels = map[string]string{}
-       }
-       pod.Labels[InherentManagedLabel] = InherentManagedLabelValue
-}
-
 func ensureEnvVar(envs []corev1.EnvVar, desired corev1.EnvVar) []corev1.EnvVar 
{
        for _, env := range envs {
                if env.Name == desired.Name {
@@ -714,7 +696,6 @@ func reorderPod(pod *corev1.Pod, req InjectionParameters) 
error {
        // Proxy container should be last to ensure `kubectl exec` and similar 
commands
        // continue to default to the user's container
        pod.Spec.Containers = modifyContainers(pod.Spec.Containers, 
ProxyContainerName, MoveLast)
-       pod.Spec.Containers = modifyContainers(pod.Spec.Containers, 
InherentGRPCInboundContainerName, MoveLast)
        return nil
 }
 
@@ -730,38 +711,6 @@ func createPatch(pod *corev1.Pod, original []byte) 
([]byte, error) {
        return json.Marshal(p)
 }
 
-func createServicePatch(svc *corev1.Service, original []byte) ([]byte, error) {
-       reinjected, err := json.Marshal(svc)
-       if err != nil {
-               return nil, err
-       }
-       p, err := jsonpatch.CreatePatch(original, reinjected)
-       if err != nil {
-               return nil, err
-       }
-       return json.Marshal(p)
-}
-
-func rewriteInherentServiceTargetPorts(svc *corev1.Service) bool {
-       if svc.Spec.Type == corev1.ServiceTypeExternalName || 
len(svc.Spec.Selector) == 0 {
-               return false
-       }
-
-       changed := false
-       for i := range svc.Spec.Ports {
-               port := &svc.Spec.Ports[i]
-               if port.Protocol != "" && port.Protocol != corev1.ProtocolTCP {
-                       continue
-               }
-               if port.TargetPort.Type == intstr.Int && port.TargetPort.IntVal 
== InherentGRPCInboundPort {
-                       continue
-               }
-               port.TargetPort = intstr.FromInt(InherentGRPCInboundPort)
-               changed = true
-       }
-       return changed
-}
-
 func applyOverlayYAML(target *corev1.Pod, overlayYAML []byte) (*corev1.Pod, 
error) {
        currentJSON, err := json.Marshal(target)
        if err != nil {
diff --git a/samples/addons/grafana.yaml b/samples/addons/grafana.yaml
index 98651b8c..a1a5f2e8 100644
--- a/samples/addons/grafana.yaml
+++ b/samples/addons/grafana.yaml
@@ -826,6 +826,193 @@ data:
       "version": 1,
       "weekStart": ""
     }
+  application-standard-metrics.json: |
+    {
+      "annotations": {
+        "list": []
+      },
+      "editable": true,
+      "graphTooltip": 0,
+      "id": null,
+      "links": [],
+      "panels": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "prometheus"
+          },
+          "fieldConfig": {
+            "defaults": {
+              "unit": "reqps"
+            },
+            "overrides": []
+          },
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 0
+          },
+          "id": 1,
+          "options": {
+            "legend": {
+              "displayMode": "list",
+              "placement": "bottom",
+              "showLegend": true
+            },
+            "tooltip": {
+              "mode": "multi",
+              "sort": "none"
+            }
+          },
+          "targets": [
+            {
+              "expr": "sum by (reporter) 
(rate(dubbo_inherent_requests_total[1m]))",
+              "legendFormat": "{{reporter}}",
+              "refId": "A"
+            }
+          ],
+          "title": "Request Rate",
+          "type": "timeseries"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "prometheus"
+          },
+          "fieldConfig": {
+            "defaults": {
+              "unit": "s"
+            },
+            "overrides": []
+          },
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 0
+          },
+          "id": 2,
+          "options": {
+            "legend": {
+              "displayMode": "list",
+              "placement": "bottom",
+              "showLegend": true
+            },
+            "tooltip": {
+              "mode": "multi",
+              "sort": "none"
+            }
+          },
+          "targets": [
+            {
+              "expr": "histogram_quantile(0.95, sum by (le, reporter) 
(rate(dubbo_inherent_request_duration_seconds_bucket[5m])))",
+              "legendFormat": "{{reporter}}",
+              "refId": "A"
+            }
+          ],
+          "title": "Request Duration P95",
+          "type": "timeseries"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "prometheus"
+          },
+          "fieldConfig": {
+            "defaults": {
+              "unit": "bytes"
+            },
+            "overrides": []
+          },
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 8
+          },
+          "id": 3,
+          "options": {
+            "legend": {
+              "displayMode": "list",
+              "placement": "bottom",
+              "showLegend": true
+            },
+            "tooltip": {
+              "mode": "multi",
+              "sort": "none"
+            }
+          },
+          "targets": [
+            {
+              "expr": "sum by (reporter) 
(rate(dubbo_inherent_request_size_bytes_sum[5m])) / sum by (reporter) 
(rate(dubbo_inherent_request_size_bytes_count[5m]))",
+              "legendFormat": "{{reporter}}",
+              "refId": "A"
+            }
+          ],
+          "title": "Average Request Size",
+          "type": "timeseries"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "prometheus"
+          },
+          "fieldConfig": {
+            "defaults": {
+              "unit": "bytes"
+            },
+            "overrides": []
+          },
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 8
+          },
+          "id": 4,
+          "options": {
+            "legend": {
+              "displayMode": "list",
+              "placement": "bottom",
+              "showLegend": true
+            },
+            "tooltip": {
+              "mode": "multi",
+              "sort": "none"
+            }
+          },
+          "targets": [
+            {
+              "expr": "sum by (reporter) 
(rate(dubbo_inherent_response_size_bytes_sum[5m])) / sum by (reporter) 
(rate(dubbo_inherent_response_size_bytes_count[5m]))",
+              "legendFormat": "{{reporter}}",
+              "refId": "A"
+            }
+          ],
+          "title": "Average Response Size",
+          "type": "timeseries"
+        }
+      ],
+      "refresh": "15s",
+      "schemaVersion": 39,
+      "tags": [
+        "dubbo",
+        "application",
+        "observability"
+      ],
+      "templating": {
+        "list": []
+      },
+      "time": {
+        "from": "now-30m",
+        "to": "now"
+      },
+      "timezone": "browser",
+      "title": "Application Standard Metrics",
+      "uid": "dubbo-application-standard-metrics",
+      "version": 1,
+      "weekStart": ""
+    }
 ---
 apiVersion: v1
 kind: Service
diff --git a/tests/e2e/activationapp/go.mod b/tests/e2e/activationapp/go.mod
index 8585ec70..87f18ab0 100644
--- a/tests/e2e/activationapp/go.mod
+++ b/tests/e2e/activationapp/go.mod
@@ -18,13 +18,14 @@ module dubbo.apache.org/activation-e2e
 go 1.25.0
 
 require (
+       github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3
        golang.org/x/net v0.53.0
        google.golang.org/grpc v1.82.1
+       google.golang.org/protobuf v1.36.11
 )
 
 require (
        golang.org/x/sys v0.43.0 // indirect
        golang.org/x/text v0.36.0 // indirect
        google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260414002931-afd174a4e478 // indirect
-       google.golang.org/protobuf v1.36.11 // indirect
 )
diff --git a/tests/e2e/activationapp/go.sum b/tests/e2e/activationapp/go.sum
index 7b006a0a..c8161548 100644
--- a/tests/e2e/activationapp/go.sum
+++ b/tests/e2e/activationapp/go.sum
@@ -10,6 +10,8 @@ github.com/google/go-cmp v0.7.0 
h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3 
h1:ypir1ZNYdAKOuWokpObAdUtCDXLD3yqP1AOqCURe3WU=
+github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3/go.mod 
h1:o2HDUgL1ntaDbWomZ4cD2tt8jBamuG2qRtjXOa1zZ0Q=
 go.opentelemetry.io/auto/sdk v1.2.1 
h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
 go.opentelemetry.io/auto/sdk v1.2.1/go.mod 
h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
 go.opentelemetry.io/otel v1.43.0 
h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
diff --git a/tests/e2e/activationapp/main.go b/tests/e2e/activationapp/main.go
index 3eb7988b..0e965c22 100644
--- a/tests/e2e/activationapp/main.go
+++ b/tests/e2e/activationapp/main.go
@@ -16,16 +16,21 @@
 package main
 
 import (
+       "context"
        "log"
+       "net"
        "net/http"
        "os"
        "time"
 
+       runtimeapplication "github.com/kdubbo/xds-api/grpc/application"
        "golang.org/x/net/http2"
        "golang.org/x/net/http2/h2c"
        "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials/insecure"
        "google.golang.org/grpc/health"
        healthpb "google.golang.org/grpc/health/grpc_health_v1"
+       "google.golang.org/protobuf/types/known/emptypb"
 )
 
 func main() {
@@ -35,6 +40,8 @@ func main() {
        switch os.Args[1] {
        case "server":
                runServer()
+       case "telemetry":
+               runTelemetryApplication()
        case "sleep":
                time.Sleep(5 * time.Second)
        default:
@@ -58,3 +65,49 @@ func runServer() {
        log.Printf("SERVING address=:8080")
        log.Fatal(http.ListenAndServe(":8080", handler))
 }
+
+func runTelemetryApplication() {
+       listener, err := net.Listen("tcp", ":8080")
+       if err != nil {
+               log.Fatal(err)
+       }
+       grpcServer, err := runtimeapplication.NewServer()
+       if err != nil {
+               log.Fatal(err)
+       }
+       checker := health.NewServer()
+       checker.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
+       checker.SetServingStatus("telemetry-load", 
healthpb.HealthCheckResponse_SERVING)
+       healthpb.RegisterHealthServer(grpcServer, checker)
+       go func() {
+               log.Printf("SERVING grpc=:8080")
+               log.Fatal(grpcServer.Serve(listener))
+       }()
+
+       connection, err := runtimeapplication.NewClient(
+               "passthrough:///127.0.0.1:8080",
+               grpc.WithTransportCredentials(insecure.NewCredentials()),
+       )
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer connection.Close()
+
+       client := healthpb.NewHealthClient(connection)
+       ticker := time.NewTicker(time.Second)
+       defer ticker.Stop()
+       for range ticker.C {
+               ctx, cancel := context.WithTimeout(context.Background(), 
time.Second)
+               _, successErr := client.Check(ctx, 
&healthpb.HealthCheckRequest{Service: "telemetry-load"})
+               // The unknown method supplies a stable non-OK status for label 
and
+               // aggregation verification without opening another connection.
+               failureErr := connection.Invoke(ctx, 
"/telemetry.v1.Probe/Missing", &emptypb.Empty{}, &emptypb.Empty{})
+               cancel()
+               if successErr != nil {
+                       log.Printf("health request failed: %v", successErr)
+               }
+               if failureErr == nil {
+                       log.Printf("unknown method unexpectedly succeeded")
+               }
+       }
+}
diff --git a/tests/e2e/testdata/telemetry-application.yaml 
b/tests/e2e/testdata/telemetry-application.yaml
new file mode 100644
index 00000000..1e1a5ec2
--- /dev/null
+++ b/tests/e2e/testdata/telemetry-application.yaml
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0.
+
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: telemetry-e2e
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: telemetry-application
+  namespace: telemetry-e2e
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: telemetry-application
+  namespace: telemetry-e2e
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: telemetry-application
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/name: telemetry-application
+        inherent.dubbo.apache.org/inject: "true"
+      annotations:
+        inject.dubbo.apache.org/templates: grpc-engine
+    spec:
+      serviceAccountName: telemetry-application
+      containers:
+      - name: application
+        image: kdubbo/activation-e2e:telemetry-auto-r2
+        imagePullPolicy: IfNotPresent
+        args:
+        - telemetry
+        ports:
+        - name: grpc
+          containerPort: 8080
+        readinessProbe:
+          httpGet:
+            path: /healthz
+            port: metrics
+          periodSeconds: 2

Reply via email to