This is an automated email from the ASF dual-hosted git repository.
mfordjody 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 94776147 Add egress gateway and adjustment observability (#930)
94776147 is described below
commit 94776147f3ec2ba51fd3e80cb4c45793a2a902fc
Author: mfordjody <[email protected]>
AuthorDate: Mon Jun 29 15:38:16 2026 +0800
Add egress gateway and adjustment observability (#930)
---
cli/cmd/dashboard.go | 38 ++++-
cli/cmd/dashboard_test.go | 30 ++++
.../pkg/bootstrap/proxyless_grpc_controller.go | 2 +-
dubbod/discovery/pkg/bootstrap/server.go | 3 +
.../pkg/config/kube/crdclient/types.gen.go | 51 +++++++
.../config/kube/gateway/deployment_controller.go | 117 ++++++++++++++-
.../kube/gateway/deployment_controller_test.go | 119 +++++++++++++++-
dubbod/discovery/pkg/model/push_context.go | 86 ++++++++++-
dubbod/discovery/pkg/networking/grpcgen/cds.go | 28 ++++
.../discovery/pkg/networking/grpcgen/cds_test.go | 91 ++++++++++++
.../pkg/xds/endpoints/endpoint_builder.go | 30 ++++
.../pkg/xds/endpoints/endpoint_builder_test.go | 20 +++
manifests/charts/dubbod/files/kube-gateway.yaml | 7 +
pkg/config/schema/collections/collections.gen.go | 22 +++
pkg/config/schema/gvk/resources.gen.go | 10 ++
pkg/config/schema/gvr/resources.gen.go | 6 +
pkg/config/schema/kind/resources.gen.go | 5 +
pkg/config/schema/kubeclient/resources.gen.go | 13 ++
pkg/config/schema/kubetypes/resources.gen.go | 2 +
pkg/config/schema/metadata.yaml | 11 ++
samples/addons/grafana.yaml | 157 +++++++++++++++++++++
samples/addons/prometheus.yaml | 51 +++++++
samples/addons/{prometheus.yaml => tracing.yaml} | 112 ++++++---------
23 files changed, 920 insertions(+), 91 deletions(-)
diff --git a/cli/cmd/dashboard.go b/cli/cmd/dashboard.go
index 4705c691..2e062f06 100644
--- a/cli/cmd/dashboard.go
+++ b/cli/cmd/dashboard.go
@@ -88,7 +88,7 @@ func DashboardCmd(ctx cli.Context) *cobra.Command {
}
if args.wait > 0 {
- if err := waitForDashboard(cmd.Context(),
client.Kube(), dashboardNamespace, args.wait); err != nil {
+ if err := waitForDashboard(cmd.Context(),
client.Kube(), dashboardNamespace, dashboardWaitDeploymentNames(files),
args.wait); err != nil {
return err
}
if _, err := fmt.Fprintln(cmd.OutOrStdout(),
"dashboard ready"); err != nil {
@@ -97,11 +97,17 @@ func DashboardCmd(ctx cli.Context) *cobra.Command {
}
_, err = fmt.Fprintf(cmd.OutOrStdout(), "prometheus:
kubectl -n %s port-forward svc/prometheus 9090:9090\ngrafana: kubectl -n %s
port-forward svc/grafana 3000:3000\n", dashboardNamespace, dashboardNamespace)
+ if err != nil {
+ return err
+ }
+ if dashboardHasTracing(files) {
+ _, err = fmt.Fprintf(cmd.OutOrStdout(),
"tracing: kubectl -n %s port-forward svc/tracing 16686:16686\n",
dashboardNamespace)
+ }
return err
},
}
command.Flags().StringVar(&args.manifests, "manifests", args.manifests,
"Dashboard manifest file or samples/addons directory")
- command.Flags().DurationVar(&args.wait, "wait", args.wait, "Maximum
time to wait for Prometheus and Grafana deployments; 0 disables waiting")
+ command.Flags().DurationVar(&args.wait, "wait", args.wait, "Maximum
time to wait for observability deployments; 0 disables waiting")
return command
}
@@ -126,7 +132,29 @@ func dashboardManifestFiles(path string) ([]string, error)
{
if _, err := os.Stat(grafana); err != nil {
return nil, fmt.Errorf("dashboard grafana manifest not found in
%s", path)
}
- return []string{prometheus, grafana}, nil
+ files := []string{prometheus, grafana}
+ tracing := filepath.Join(path, "tracing.yaml")
+ if _, err := os.Stat(tracing); err == nil {
+ files = append(files, tracing)
+ }
+ return files, nil
+}
+
+func dashboardHasTracing(files []string) bool {
+ for _, file := range files {
+ if filepath.Base(file) == "tracing.yaml" {
+ return true
+ }
+ }
+ return false
+}
+
+func dashboardWaitDeploymentNames(files []string) []string {
+ names := []string{"prometheus", "grafana"}
+ if dashboardHasTracing(files) {
+ names = append(names, "tracing")
+ }
+ return names
}
func applyManifestFile(ctx context.Context, client kube.CLIClient, file
string) ([]appliedObject, error) {
@@ -226,8 +254,8 @@ func printAppliedDashboard(writer io.Writer, objects
[]appliedObject) error {
return nil
}
-func waitForDashboard(ctx context.Context, client kubernetes.Interface,
namespace string, timeout time.Duration) error {
- for _, name := range []string{"prometheus", "grafana"} {
+func waitForDashboard(ctx context.Context, client kubernetes.Interface,
namespace string, names []string, timeout time.Duration) error {
+ for _, name := range names {
if err := waitForAvailableDeployment(ctx, client, namespace,
name, timeout); err != nil {
return err
}
diff --git a/cli/cmd/dashboard_test.go b/cli/cmd/dashboard_test.go
index f9105dfb..a087463a 100644
--- a/cli/cmd/dashboard_test.go
+++ b/cli/cmd/dashboard_test.go
@@ -26,6 +26,36 @@ func TestDashboardManifestFiles(t *testing.T) {
if !reflect.DeepEqual(got, want) {
t.Fatalf("dashboardManifestFiles() = %v, want %v", got, want)
}
+ if dashboardHasTracing(got) {
+ t.Fatalf("dashboardHasTracing() = true, want false")
+ }
+ if names := dashboardWaitDeploymentNames(got);
!reflect.DeepEqual(names, []string{"prometheus", "grafana"}) {
+ t.Fatalf("dashboardWaitDeploymentNames() = %v", names)
+ }
+}
+
+func TestDashboardManifestFilesIncludesTracingWhenPresent(t *testing.T) {
+ dir := t.TempDir()
+ for _, name := range []string{"prometheus.yaml", "grafana.yaml",
"tracing.yaml"} {
+ if err := os.WriteFile(filepath.Join(dir, name),
[]byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: "+name+"\n"),
0o600); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ got, err := dashboardManifestFiles(dir)
+ if err != nil {
+ t.Fatalf("dashboardManifestFiles() returned error: %v", err)
+ }
+ want := []string{filepath.Join(dir, "prometheus.yaml"),
filepath.Join(dir, "grafana.yaml"), filepath.Join(dir, "tracing.yaml")}
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("dashboardManifestFiles() = %v, want %v", got, want)
+ }
+ if !dashboardHasTracing(got) {
+ t.Fatalf("dashboardHasTracing() = false, want true")
+ }
+ if names := dashboardWaitDeploymentNames(got);
!reflect.DeepEqual(names, []string{"prometheus", "grafana", "tracing"}) {
+ t.Fatalf("dashboardWaitDeploymentNames() = %v", names)
+ }
}
func TestReadManifestObjects(t *testing.T) {
diff --git a/dubbod/discovery/pkg/bootstrap/proxyless_grpc_controller.go
b/dubbod/discovery/pkg/bootstrap/proxyless_grpc_controller.go
index 57b68813..bd2f512b 100644
--- a/dubbod/discovery/pkg/bootstrap/proxyless_grpc_controller.go
+++ b/dubbod/discovery/pkg/bootstrap/proxyless_grpc_controller.go
@@ -238,7 +238,7 @@ func proxylessGRPCRuntimeConfigNeedsUpdate(req
*discoverymodel.PushRequest) bool
}
for cfg := range req.ConfigsUpdated {
switch cfg.Kind {
- case kind.HTTPRoute, kind.CircuitBreakerPolicy,
kind.PeerAuthentication, kind.RequestAuthentication, kind.AuthorizationPolicy,
kind.Service, kind.EndpointSlice, kind.Endpoints, kind.Pod, kind.Namespace:
+ case kind.HTTPRoute, kind.BackendTLSPolicy,
kind.CircuitBreakerPolicy, kind.PeerAuthentication, kind.RequestAuthentication,
kind.AuthorizationPolicy, kind.Service, kind.EndpointSlice, kind.Endpoints,
kind.Pod, kind.Namespace:
return true
}
}
diff --git a/dubbod/discovery/pkg/bootstrap/server.go
b/dubbod/discovery/pkg/bootstrap/server.go
index f17a7827..d904759d 100644
--- a/dubbod/discovery/pkg/bootstrap/server.go
+++ b/dubbod/discovery/pkg/bootstrap/server.go
@@ -511,6 +511,8 @@ func (s *Server) initRegistryEventHandlers() {
configKind = kind.KubernetesGateway
case "HTTPRoute":
configKind = kind.HTTPRoute
+ case "BackendTLSPolicy":
+ configKind = kind.BackendTLSPolicy
case "CircuitBreakerPolicy":
configKind = kind.CircuitBreakerPolicy
default:
@@ -535,6 +537,7 @@ func (s *Server) initRegistryEventHandlers() {
configKind == kind.RequestAuthentication ||
configKind == kind.AuthorizationPolicy ||
configKind == kind.HTTPRoute ||
+ configKind == kind.BackendTLSPolicy ||
configKind == kind.CircuitBreakerPolicy
// Trigger ConfigUpdate to push changes to all connected proxies
diff --git a/dubbod/discovery/pkg/config/kube/crdclient/types.gen.go
b/dubbod/discovery/pkg/config/kube/crdclient/types.gen.go
index c5bf20ef..3d9de0e7 100755
--- a/dubbod/discovery/pkg/config/kube/crdclient/types.gen.go
+++ b/dubbod/discovery/pkg/config/kube/crdclient/types.gen.go
@@ -37,6 +37,11 @@ func create(c kube.Client, cfg config.Config, objMeta
metav1.ObjectMeta) (metav1
ObjectMeta: objMeta,
Spec:
*(cfg.Spec.(*githubcomkdubboapisecurityv1alpha3.AuthorizationPolicy)),
}, metav1.CreateOptions{})
+ case gvk.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(cfg.Namespace).Create(context.TODO(),
&sigsk8siogatewayapiapisv1.BackendTLSPolicy{
+ ObjectMeta: objMeta,
+ Spec:
*(cfg.Spec.(*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec)),
+ }, metav1.CreateOptions{})
case gvk.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(cfg.Namespace).Create(context.TODO(),
&apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy{
ObjectMeta: objMeta,
@@ -79,6 +84,11 @@ func update(c kube.Client, cfg config.Config, objMeta
metav1.ObjectMeta) (metav1
ObjectMeta: objMeta,
Spec:
*(cfg.Spec.(*githubcomkdubboapisecurityv1alpha3.AuthorizationPolicy)),
}, metav1.UpdateOptions{})
+ case gvk.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(cfg.Namespace).Update(context.TODO(),
&sigsk8siogatewayapiapisv1.BackendTLSPolicy{
+ ObjectMeta: objMeta,
+ Spec:
*(cfg.Spec.(*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec)),
+ }, metav1.UpdateOptions{})
case gvk.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(cfg.Namespace).Update(context.TODO(),
&apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy{
ObjectMeta: objMeta,
@@ -121,6 +131,11 @@ func updateStatus(c kube.Client, cfg config.Config,
objMeta metav1.ObjectMeta) (
ObjectMeta: objMeta,
Status:
*(cfg.Status.(*githubcomkdubboapimetav1alpha1.DubboStatus)),
}, metav1.UpdateOptions{})
+ case gvk.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(cfg.Namespace).UpdateStatus(context.TODO(),
&sigsk8siogatewayapiapisv1.BackendTLSPolicy{
+ ObjectMeta: objMeta,
+ Status:
*(cfg.Status.(*sigsk8siogatewayapiapisv1.PolicyStatus)),
+ }, metav1.UpdateOptions{})
case gvk.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(cfg.Namespace).UpdateStatus(context.TODO(),
&apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy{
ObjectMeta: objMeta,
@@ -176,6 +191,21 @@ func patch(c kube.Client, orig config.Config, origMeta
metav1.ObjectMeta, mod co
}
return
c.Dubbo().SecurityV1alpha3().AuthorizationPolicies(orig.Namespace).
Patch(context.TODO(), orig.Name, typ, patchBytes,
metav1.PatchOptions{FieldManager: "pilot-discovery"})
+ case gvk.BackendTLSPolicy:
+ oldRes := &sigsk8siogatewayapiapisv1.BackendTLSPolicy{
+ ObjectMeta: origMeta,
+ Spec:
*(orig.Spec.(*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec)),
+ }
+ modRes := &sigsk8siogatewayapiapisv1.BackendTLSPolicy{
+ ObjectMeta: modMeta,
+ Spec:
*(mod.Spec.(*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec)),
+ }
+ patchBytes, err := genPatchBytes(oldRes, modRes, typ)
+ if err != nil {
+ return nil, err
+ }
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(orig.Namespace).
+ Patch(context.TODO(), orig.Name, typ, patchBytes,
metav1.PatchOptions{FieldManager: "pilot-discovery"})
case gvk.CircuitBreakerPolicy:
oldRes :=
&apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy{
ObjectMeta: origMeta,
@@ -279,6 +309,8 @@ func delete(c kube.Client, typ config.GroupVersionKind,
name, namespace string,
switch typ {
case gvk.AuthorizationPolicy:
return
c.Dubbo().SecurityV1alpha3().AuthorizationPolicies(namespace).Delete(context.TODO(),
name, deleteOptions)
+ case gvk.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(namespace).Delete(context.TODO(),
name, deleteOptions)
case gvk.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(namespace).Delete(context.TODO(),
name, deleteOptions)
case gvk.GatewayClass:
@@ -316,6 +348,25 @@ var translationMap = map[config.GroupVersionKind]func(r
runtime.Object) config.C
Status: &obj.Status,
}
},
+ gvk.BackendTLSPolicy: func(r runtime.Object) config.Config {
+ obj := r.(*sigsk8siogatewayapiapisv1.BackendTLSPolicy)
+ return config.Config{
+ Meta: config.Meta{
+ GroupVersionKind: gvk.BackendTLSPolicy,
+ Name: obj.Name,
+ Namespace: obj.Namespace,
+ Labels: obj.Labels,
+ Annotations: obj.Annotations,
+ ResourceVersion: obj.ResourceVersion,
+ CreationTimestamp: obj.CreationTimestamp.Time,
+ OwnerReferences: obj.OwnerReferences,
+ UID: string(obj.UID),
+ Generation: obj.Generation,
+ },
+ Spec: &obj.Spec,
+ Status: &obj.Status,
+ }
+ },
gvk.CircuitBreakerPolicy: func(r runtime.Object) config.Config {
obj :=
r.(*apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy)
return config.Config{
diff --git a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
index 324e07ec..6b9362ce 100644
--- a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
+++ b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
@@ -79,6 +79,7 @@ const (
serviceTargetPortAnnotation = "gateway.dubbo.apache.org/target-port"
serviceNodePortAnnotation = "gateway.dubbo.apache.org/node-port"
xdsAddressAnnotation = "gateway.dubbo.apache.org/xds-address"
+ otelEndpointAnnotation = "gateway.dubbo.apache.org/otel-endpoint"
)
var builtinClasses = getBuiltinClasses()
@@ -124,6 +125,7 @@ type DeploymentController struct {
configMaps kclient.Client[*corev1.ConfigMap]
httpRoutes kclient.Client[*gateway.HTTPRoute]
circuitBreakers kclient.Client[*clientnetworking.CircuitBreakerPolicy]
+ backendTLS kclient.Client[*gateway.BackendTLSPolicy]
namespaces kclient.Client[*corev1.Namespace]
tagWatcher TagWatcher
revision string
@@ -207,6 +209,11 @@ func NewDeploymentController(
dc.services = kclient.NewFiltered[*corev1.Service](client, filter)
dc.services.AddEventHandler(parentHandler)
+ dc.services.AddEventHandler(controllers.ObjectHandler(func(o
controllers.Object) {
+ for _, gw := range dc.gateways.List(metav1.NamespaceAll,
klabels.Everything()) {
+ dc.queue.AddObject(gw)
+ }
+ }))
dc.clients[gvr.Service] = NewUntypedWrapper(dc.services)
dc.deployments = kclient.NewFiltered[*appsv1.Deployment](client, filter)
@@ -235,6 +242,13 @@ func NewDeploymentController(
}
}))
+ dc.backendTLS = kclient.NewFiltered[*gateway.BackendTLSPolicy](client,
filter)
+ dc.backendTLS.AddEventHandler(controllers.ObjectHandler(func(o
controllers.Object) {
+ for _, gw := range dc.gateways.List(metav1.NamespaceAll,
klabels.Everything()) {
+ dc.queue.AddObject(gw)
+ }
+ }))
+
// Namespace is a cluster-scoped resource, use New instead of
NewFiltered
dc.namespaces = kclient.New[*corev1.Namespace](client)
dc.namespaces.AddEventHandler(controllers.ObjectHandler(func(o
controllers.Object) {
@@ -285,6 +299,7 @@ func (d *DeploymentController) Run(stop <-chan struct{}) {
d.configMaps.HasSynced,
d.httpRoutes.HasSynced,
d.circuitBreakers.HasSynced,
+ d.backendTLS.HasSynced,
d.gateways.HasSynced,
d.gatewayClasses.HasSynced,
)
@@ -301,6 +316,7 @@ func (d *DeploymentController) Run(stop <-chan struct{}) {
d.configMaps,
d.httpRoutes,
d.circuitBreakers,
+ d.backendTLS,
d.gateways,
d.gatewayClasses,
)
@@ -386,6 +402,7 @@ func (d *DeploymentController) configureGateway(log
*dubbolog.Logger, gw gateway
SystemNamespace: d.systemNamespace,
ClusterID: string(d.clusterID),
DomainSuffix: d.domainSuffix(),
+ OtelEndpoint:
strings.TrimSpace(gw.Annotations[otelEndpointAnnotation]),
}
log.Infof("desired dxgate deployment=%s/%s gatewayClass=%s
serviceType=%s ports=%s image=%s",
@@ -438,6 +455,7 @@ type TemplateInput struct {
SystemNamespace string
ClusterID string
DomainSuffix string
+ OtelEndpoint string
}
type dxgateBootstrapConfig struct {
@@ -497,10 +515,16 @@ type dxgateWeightedCluster struct {
type dxgateCluster struct {
Name string `json:"name" yaml:"name"`
Endpoints []dxgateEndpoint `json:"endpoints"
yaml:"endpoints"`
+ TLS *dxgateUpstreamTLS `json:"tls,omitempty"
yaml:"tls,omitempty"`
CircuitBreaker *dxgateCircuitBreaker
`json:"circuit_breaker,omitempty" yaml:"circuit_breaker,omitempty"`
Outlier *dxgateOutlierDetection
`json:"outlier_detection,omitempty" yaml:"outlier_detection,omitempty"`
}
+type dxgateUpstreamTLS struct {
+ Mode string `json:"mode" yaml:"mode"`
+ SNI string `json:"sni,omitempty" yaml:"sni,omitempty"`
+}
+
type dxgateCircuitBreaker struct {
MaxConnections int32 `json:"max_connections,omitempty"
yaml:"max_connections,omitempty"`
HTTP1MaxPendingRequests int32
`json:"http1_max_pending_requests,omitempty"
yaml:"http1_max_pending_requests,omitempty"`
@@ -601,7 +625,9 @@ func formatGatewayServicePorts(ports []corev1.ServicePort)
string {
func (d *DeploymentController) buildDxgateRuntimeConfig(gw gateway.Gateway)
(string, string, error) {
routes := d.httpRoutes.List(metav1.NamespaceAll, klabels.Everything())
- return buildDxgateRuntimeConfig(gw, routes,
d.circuitBreakerPolicyConfigs(), d.domainSuffix())
+ services := d.services.List(metav1.NamespaceAll, klabels.Everything())
+ backendTLS := d.backendTLSPolicies()
+ return buildDxgateRuntimeConfig(gw, routes, services, backendTLS,
d.circuitBreakerPolicyConfigs(), d.domainSuffix())
}
func (d *DeploymentController) circuitBreakerPolicyConfigs() []config.Config {
@@ -634,11 +660,20 @@ func (d *DeploymentController)
circuitBreakerPolicyConfigs() []config.Config {
return nil
}
-func buildDxgateRuntimeConfig(gw gateway.Gateway, routes []*gateway.HTTPRoute,
policies []config.Config, domainSuffix string) (string, string, error) {
+func (d *DeploymentController) backendTLSPolicies()
[]*gateway.BackendTLSPolicy {
+ if d.backendTLS != nil {
+ return d.backendTLS.List(metav1.NamespaceAll,
klabels.Everything())
+ }
+ return nil
+}
+
+func buildDxgateRuntimeConfig(gw gateway.Gateway, routes []*gateway.HTTPRoute,
services []*corev1.Service, backendTLSPolicies []*gateway.BackendTLSPolicy,
policies []config.Config, domainSuffix string) (string, string, error) {
if domainSuffix == "" {
domainSuffix = constants.DefaultClusterLocalDomain
}
circuitBreakers := circuitBreakerPoliciesByTarget(policies)
+ backendTLS := backendTLSPoliciesByTarget(backendTLSPolicies)
+ servicesByKey := servicesByNamespacedName(services)
sort.Slice(routes, func(i, j int) bool {
if routes[i].Namespace != routes[j].Namespace {
@@ -666,7 +701,7 @@ func buildDxgateRuntimeConfig(gw gateway.Gateway, routes
[]*gateway.HTTPRoute, p
if !httpRouteReferencesGateway(hr, &gw) {
continue
}
- vh, clusters := buildDxgateVirtualHost(gw, hr, circuitBreakers,
domainSuffix)
+ vh, clusters := buildDxgateVirtualHost(gw, hr, servicesByKey,
backendTLS, circuitBreakers, domainSuffix)
if len(vh.Routes) == 0 {
continue
}
@@ -700,7 +735,7 @@ func dxgateRuntimeVersion(gw gateway.Gateway, routes
[]*gateway.HTTPRoute) strin
return strings.Join(parts, ";")
}
-func buildDxgateVirtualHost(gw gateway.Gateway, hr *gateway.HTTPRoute,
circuitBreakers map[string]dxgateBackendCircuitBreaker, domainSuffix string)
(dxgateVirtualHost, []dxgateCluster) {
+func buildDxgateVirtualHost(gw gateway.Gateway, hr *gateway.HTTPRoute,
services map[string]*corev1.Service, backendTLS map[string]*dxgateUpstreamTLS,
circuitBreakers map[string]dxgateBackendCircuitBreaker, domainSuffix string)
(dxgateVirtualHost, []dxgateCluster) {
vh := dxgateVirtualHost{
Name: fmt.Sprintf("%s-%s", hr.Namespace, hr.Name),
Domains: dxgateRouteDomains(gw, hr),
@@ -733,7 +768,9 @@ func buildDxgateVirtualHost(gw gateway.Gateway, hr
*gateway.HTTPRoute, circuitBr
}
port := uint16(*backendRef.Port)
clusterName := fmt.Sprintf("%s-%s-%d-%d",
hr.Namespace, hr.Name, ruleIdx, backendIdx)
- policy :=
circuitBreakers[namespacedServiceKey(backendNamespace, string(backendRef.Name))]
+ backendKey :=
namespacedServiceKey(backendNamespace, string(backendRef.Name))
+ policy := circuitBreakers[backendKey]
+ upstreamTLS := backendTLS[backendKey]
route.WeightedClusters =
append(route.WeightedClusters, dxgateWeightedCluster{
Name: clusterName,
@@ -741,11 +778,12 @@ func buildDxgateVirtualHost(gw gateway.Gateway, hr
*gateway.HTTPRoute, circuitBr
})
clusters = append(clusters, dxgateCluster{
Name: clusterName,
+ TLS: upstreamTLS,
CircuitBreaker: policy.CircuitBreaker,
Outlier: policy.Outlier,
Endpoints: []dxgateEndpoint{
{
- Address:
fmt.Sprintf("%s.%s.svc.%s", backendRef.Name, backendNamespace, domainSuffix),
+ Address:
dxgateBackendAddress(backendNamespace, string(backendRef.Name), domainSuffix,
services),
Port: port,
Healthy: true,
},
@@ -760,6 +798,73 @@ func buildDxgateVirtualHost(gw gateway.Gateway, hr
*gateway.HTTPRoute, circuitBr
return vh, clusters
}
+func servicesByNamespacedName(services []*corev1.Service)
map[string]*corev1.Service {
+ out := map[string]*corev1.Service{}
+ for _, svc := range services {
+ if svc == nil {
+ continue
+ }
+ out[namespacedServiceKey(svc.Namespace, svc.Name)] = svc
+ }
+ return out
+}
+
+func dxgateBackendAddress(namespace, name, domainSuffix string, services
map[string]*corev1.Service) string {
+ if svc := services[namespacedServiceKey(namespace, name)]; svc != nil
&& svc.Spec.Type == corev1.ServiceTypeExternalName && svc.Spec.ExternalName !=
"" {
+ return svc.Spec.ExternalName
+ }
+ return fmt.Sprintf("%s.%s.svc.%s", name, namespace, domainSuffix)
+}
+
+func backendTLSPoliciesByTarget(policies []*gateway.BackendTLSPolicy)
map[string]*dxgateUpstreamTLS {
+ out := map[string]*dxgateUpstreamTLS{}
+ sort.Slice(policies, func(i, j int) bool {
+ if
!policies[i].CreationTimestamp.Time.Equal(policies[j].CreationTimestamp.Time) {
+ return
policies[i].CreationTimestamp.Time.Before(policies[j].CreationTimestamp.Time)
+ }
+ if policies[i].Namespace != policies[j].Namespace {
+ return policies[i].Namespace < policies[j].Namespace
+ }
+ return policies[i].Name < policies[j].Name
+ })
+ for _, policy := range policies {
+ if policy == nil || !supportsSystemBackendTLS(policy) {
+ continue
+ }
+ upstreamTLS := &dxgateUpstreamTLS{
+ Mode: "simple",
+ SNI: string(policy.Spec.Validation.Hostname),
+ }
+ for _, target := range policy.Spec.TargetRefs {
+ if !isBackendTLSPolicyServiceTarget(target) {
+ continue
+ }
+ key := namespacedServiceKey(policy.Namespace,
string(target.Name))
+ if _, found := out[key]; !found {
+ out[key] = upstreamTLS
+ }
+ }
+ }
+ return out
+}
+
+func supportsSystemBackendTLS(policy *gateway.BackendTLSPolicy) bool {
+ if policy.Spec.Validation.Hostname == "" {
+ return false
+ }
+ wellKnown := policy.Spec.Validation.WellKnownCACertificates
+ return wellKnown != nil && *wellKnown ==
gateway.WellKnownCACertificatesSystem
+}
+
+func isBackendTLSPolicyServiceTarget(target
gateway.LocalPolicyTargetReferenceWithSectionName) bool {
+ if target.SectionName != nil {
+ return false
+ }
+ group := strings.TrimSpace(string(target.Group))
+ kind := strings.TrimSpace(string(target.Kind))
+ return (group == "" || group == "core") && strings.EqualFold(kind,
"Service")
+}
+
type dxgateBackendCircuitBreaker struct {
CircuitBreaker *dxgateCircuitBreaker
Outlier *dxgateOutlierDetection
diff --git
a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
index 1764c6e6..da3cef09 100644
--- a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
+++ b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
@@ -122,7 +122,7 @@ func TestBuildDxgateRuntimeConfigFromHTTPRoute(t
*testing.T) {
},
}
- raw, hash, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, nil, "cluster.local")
+ raw, hash, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, nil, nil, nil, "cluster.local")
if err != nil {
t.Fatal(err)
}
@@ -220,7 +220,7 @@ func
TestBuildDxgateRuntimeConfigAppliesCircuitBreakerPolicy(t *testing.T) {
},
}
- raw, _, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, policies, "cluster.local")
+ raw, _, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, nil, nil, policies, "cluster.local")
if err != nil {
t.Fatal(err)
}
@@ -278,7 +278,7 @@ func
TestBuildDxgateRuntimeConfigFiltersUnattachedHTTPRoutes(t *testing.T) {
},
}
- raw, _, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, nil, "cluster.local")
+ raw, _, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, nil, nil, nil, "cluster.local")
if err != nil {
t.Fatal(err)
}
@@ -291,6 +291,96 @@ func
TestBuildDxgateRuntimeConfigFiltersUnattachedHTTPRoutes(t *testing.T) {
}
}
+func TestBuildDxgateRuntimeConfigExternalNameBackendTLS(t *testing.T) {
+ backendPort := gatewayv1.PortNumber(443)
+ hostname := gatewayv1.Hostname("httpbin-egress.app.svc.cluster.local")
+ wellKnown := gatewayv1.WellKnownCACertificatesSystem
+ gw := gatewayv1.Gateway{
+ ObjectMeta: metav1.ObjectMeta{Name: "egress", Namespace: "app",
ResourceVersion: "10"},
+ Spec: gatewayv1.GatewaySpec{
+ GatewayClassName: "dubbo",
+ Listeners: []gatewayv1.Listener{
+ {Name: "http", Protocol:
gatewayv1.HTTPProtocolType, Port: 80},
+ },
+ },
+ }
+ route := &gatewayv1.HTTPRoute{
+ ObjectMeta: metav1.ObjectMeta{Name: "httpbin", Namespace:
"app", ResourceVersion: "20"},
+ Spec: gatewayv1.HTTPRouteSpec{
+ CommonRouteSpec: gatewayv1.CommonRouteSpec{
+ ParentRefs: []gatewayv1.ParentReference{{Name:
"egress"}},
+ },
+ Hostnames: []gatewayv1.Hostname{hostname},
+ Rules: []gatewayv1.HTTPRouteRule{
+ {
+ BackendRefs: []gatewayv1.HTTPBackendRef{
+ {
+ BackendRef:
gatewayv1.BackendRef{
+
BackendObjectReference: gatewayv1.BackendObjectReference{
+ Name:
"httpbin-egress",
+ Port:
&backendPort,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ service := &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "httpbin-egress",
Namespace: "app"},
+ Spec: corev1.ServiceSpec{
+ Type: corev1.ServiceTypeExternalName,
+ ExternalName: "httpbin.org",
+ Ports: []corev1.ServicePort{{Name: "https",
Port: 443}},
+ },
+ }
+ policy := &gatewayv1.BackendTLSPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "httpbin-tls",
+ Namespace: "app",
+ CreationTimestamp: metav1.NewTime(time.Unix(1, 0)),
+ },
+ Spec: gatewayv1.BackendTLSPolicySpec{
+ TargetRefs:
[]gatewayv1.LocalPolicyTargetReferenceWithSectionName{
+ {
+ LocalPolicyTargetReference:
gatewayv1.LocalPolicyTargetReference{
+ Group: gatewayv1.Group(""),
+ Kind:
gatewayv1.Kind("Service"),
+ Name:
gatewayv1.ObjectName("httpbin-egress"),
+ },
+ },
+ },
+ Validation: gatewayv1.BackendTLSPolicyValidation{
+ WellKnownCACertificates: &wellKnown,
+ Hostname:
gatewayv1.PreciseHostname("httpbin.org"),
+ },
+ },
+ }
+
+ raw, _, err := buildDxgateRuntimeConfig(gw,
[]*gatewayv1.HTTPRoute{route}, []*corev1.Service{service},
[]*gatewayv1.BackendTLSPolicy{policy}, nil, "cluster.local")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var cfg dxgateRuntimeConfig
+ if err := yaml.Unmarshal([]byte(raw), &cfg); err != nil {
+ t.Fatal(err)
+ }
+ if len(cfg.Clusters) != 1 {
+ t.Fatalf("clusters = %d, want 1", len(cfg.Clusters))
+ }
+ cluster := cfg.Clusters[0]
+ if got := cluster.Endpoints[0].Address; got != "httpbin.org" {
+ t.Fatalf("endpoint address = %q, want external DNS name", got)
+ }
+ if cluster.TLS == nil {
+ t.Fatal("cluster tls is nil")
+ }
+ if cluster.TLS.Mode != "simple" || cluster.TLS.SNI != "httpbin.org" {
+ t.Fatalf("unexpected tls config: %#v", cluster.TLS)
+ }
+}
+
func TestBuildDxgateBootstrapConfig(t *testing.T) {
raw, hash, err := buildDxgateBootstrapConfig(
"http://dubbod.dubbo-system.svc:26010",
@@ -441,7 +531,7 @@ func TestKubeGatewayTemplateRendersDxgateResources(t
*testing.T) {
return inject.Config{Templates: templates}
},
}
- rendered, err := controller.render("gateway", TemplateInput{
+ input := TemplateInput{
Gateway: &gatewayv1.Gateway{
ObjectMeta: metav1.ObjectMeta{Name: "public",
Namespace: "app"},
},
@@ -456,7 +546,8 @@ func TestKubeGatewayTemplateRendersDxgateResources(t
*testing.T) {
SystemNamespace: "dubbo-system",
ClusterID: "Kubernetes",
DomainSuffix: "cluster.local",
- })
+ }
+ rendered, err := controller.render("gateway", input)
if err != nil {
t.Fatal(err)
}
@@ -487,6 +578,14 @@ func TestKubeGatewayTemplateRendersDxgateResources(t
*testing.T) {
if !strings.Contains(rendered[2], "inject.dubbo.apache.org/templates:
grpc-engine") {
t.Fatalf("deployment pod template did not request grpc-engine
injection:\n%s", rendered[2])
}
+ if !strings.Contains(rendered[2], `prometheus.io/scrape: "true"`) ||
+ !strings.Contains(rendered[2], "prometheus.io/path: /metrics")
||
+ !strings.Contains(rendered[2], `prometheus.io/port: "26021"`) {
+ t.Fatalf("deployment pod template did not render prometheus
scrape annotations:\n%s", rendered[2])
+ }
+ if strings.Contains(rendered[2], "DXGATE_OTEL_ENDPOINT") {
+ t.Fatalf("deployment rendered OTEL endpoint without
annotation:\n%s", rendered[2])
+ }
if !strings.Contains(rendered[2], "app.kubernetes.io/instance:
public-dubbo") {
t.Fatalf("deployment did not render stable dxgate instance
label:\n%s", rendered[2])
}
@@ -499,4 +598,14 @@ func TestKubeGatewayTemplateRendersDxgateResources(t
*testing.T) {
if !strings.Contains(rendered[3], "app.kubernetes.io/instance:
public-dubbo") {
t.Fatalf("service did not render stable dxgate instance
selector:\n%s", rendered[3])
}
+
+ input.OtelEndpoint = "http://tracing.dubbo-system.svc:4317"
+ rendered, err = controller.render("gateway", input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(rendered[2], "DXGATE_OTEL_ENDPOINT") ||
+ !strings.Contains(rendered[2], `value:
"http://tracing.dubbo-system.svc:4317"`) {
+ t.Fatalf("deployment did not render dxgate OTEL endpoint:\n%s",
rendered[2])
+ }
}
diff --git a/dubbod/discovery/pkg/model/push_context.go
b/dubbod/discovery/pkg/model/push_context.go
index 6e82a0f3..b7c8a131 100644
--- a/dubbod/discovery/pkg/model/push_context.go
+++ b/dubbod/discovery/pkg/model/push_context.go
@@ -75,6 +75,7 @@ type PushContext struct {
ServiceIndex serviceIndex
virtualServiceIndex virtualServiceIndex
httpRouteIndex httpRouteIndex
+ backendTLSPolicyIndex backendTLSPolicyIndex
destinationRuleIndex destinationRuleIndex
serviceAccounts map[serviceAccountKey][]string
AuthenticationPolicies *AuthenticationPolicies
@@ -155,6 +156,14 @@ type httpRouteIndex struct {
hostToRoutes map[host.Name][]config.Config
}
+type backendTLSPolicyIndex struct {
+ serviceTLS map[string]BackendTLSSettings
+}
+
+type BackendTLSSettings struct {
+ SNI string
+}
+
type destinationRuleIndex struct {
namespaceLocal map[string]*consolidatedSubRules
exportedByNamespace map[string]*consolidatedSubRules
@@ -168,11 +177,12 @@ type consolidatedSubRules struct {
func NewPushContext() *PushContext {
return &PushContext{
- ServiceIndex: newServiceIndex(),
- virtualServiceIndex: newVirtualServiceIndex(),
- destinationRuleIndex: newDestinationRuleIndex(),
- serviceAccounts: map[serviceAccountKey][]string{},
- ProxyStatus: map[string]map[string]ProxyPushStatus{},
+ ServiceIndex: newServiceIndex(),
+ virtualServiceIndex: newVirtualServiceIndex(),
+ backendTLSPolicyIndex: backendTLSPolicyIndex{serviceTLS:
map[string]BackendTLSSettings{}},
+ destinationRuleIndex: newDestinationRuleIndex(),
+ serviceAccounts: map[serviceAccountKey][]string{},
+ ProxyStatus: map[string]map[string]ProxyPushStatus{},
}
}
@@ -589,6 +599,7 @@ func (ps *PushContext) createNewContext(env *Environment) {
// Initialize Kubernetes Gateway API resources if the controller is
enabled.
ps.initKubernetesGateways(env)
ps.initHTTPRoutes(env)
+ ps.initBackendTLSPolicies(env)
ps.initAuthenticationPolicies(env)
}
@@ -644,6 +655,15 @@ func (ps *PushContext) updateContext(env *Environment,
oldPushContext *PushConte
ps.httpRouteIndex = oldPushContext.httpRouteIndex
}
+ backendTLSPoliciesChanged := pushReq != nil &&
HasConfigsOfKind(pushReq.ConfigsUpdated, kind.BackendTLSPolicy)
+ if backendTLSPoliciesChanged {
+ log.Debugf("BackendTLSPolicies changed, re-initializing
BackendTLSPolicy index")
+ ps.initBackendTLSPolicies(env)
+ } else {
+ log.Debugf("BackendTLSPolicies unchanged, reusing old
BackendTLSPolicy index")
+ ps.backendTLSPolicyIndex = oldPushContext.backendTLSPolicyIndex
+ }
+
authnPoliciesChanged := pushReq != nil && (pushReq.Full ||
authPolicyKindsChanged(pushReq.ConfigsUpdated))
if authnPoliciesChanged || oldPushContext == nil ||
oldPushContext.AuthenticationPolicies == nil {
log.Debugf("security authentication policy changed (full=%v,
configsUpdatedContainingSecurityPolicy=%v), rebuilding authentication policies",
@@ -856,6 +876,62 @@ func (ps *PushContext) HTTPRouteForHost(hostname
host.Name) []config.Config {
return routes
}
+func (ps *PushContext) initBackendTLSPolicies(env *Environment) {
+ policies := sortConfigByCreationTime(env.List(gvk.BackendTLSPolicy,
NamespaceAll))
+ serviceTLS := map[string]BackendTLSSettings{}
+ for _, cfg := range policies {
+ spec, ok :=
cfg.Spec.(*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec)
+ if !ok {
+ log.Debugf("BackendTLSPolicy %s/%s spec is not
BackendTLSPolicySpec", cfg.Namespace, cfg.Name)
+ continue
+ }
+ if !supportsSystemBackendTLS(spec) {
+ continue
+ }
+ settings := BackendTLSSettings{SNI:
string(spec.Validation.Hostname)}
+ for _, target := range spec.TargetRefs {
+ if !isBackendTLSPolicyServiceTarget(target) {
+ continue
+ }
+ key := backendTLSPolicyServiceKey(cfg.Namespace,
string(target.Name))
+ if _, found := serviceTLS[key]; !found {
+ serviceTLS[key] = settings
+ }
+ }
+ }
+ ps.backendTLSPolicyIndex.serviceTLS = serviceTLS
+ log.Debugf("indexed BackendTLSPolicies for %d services",
len(serviceTLS))
+}
+
+func (ps *PushContext) BackendTLSForService(namespace, name string)
(BackendTLSSettings, bool) {
+ if ps == nil || ps.backendTLSPolicyIndex.serviceTLS == nil {
+ return BackendTLSSettings{}, false
+ }
+ settings, found :=
ps.backendTLSPolicyIndex.serviceTLS[backendTLSPolicyServiceKey(namespace, name)]
+ return settings, found
+}
+
+func supportsSystemBackendTLS(spec
*sigsk8siogatewayapiapisv1.BackendTLSPolicySpec) bool {
+ if spec == nil || spec.Validation.Hostname == "" {
+ return false
+ }
+ wellKnown := spec.Validation.WellKnownCACertificates
+ return wellKnown != nil && *wellKnown ==
sigsk8siogatewayapiapisv1.WellKnownCACertificatesSystem
+}
+
+func isBackendTLSPolicyServiceTarget(target
sigsk8siogatewayapiapisv1.LocalPolicyTargetReferenceWithSectionName) bool {
+ if target.SectionName != nil {
+ return false
+ }
+ group := strings.TrimSpace(string(target.Group))
+ kind := strings.TrimSpace(string(target.Kind))
+ return (group == "" || group == "core") && strings.EqualFold(kind,
"Service")
+}
+
+func backendTLSPolicyServiceKey(namespace, name string) string {
+ return namespace + "/" + name
+}
+
// sortConfigBySelectorAndCreationTime sorts the list of config objects based
on creation time.
func sortConfigBySelectorAndCreationTime(configs []config.Config)
[]config.Config {
sort.Slice(configs, func(i, j int) bool {
diff --git a/dubbod/discovery/pkg/networking/grpcgen/cds.go
b/dubbod/discovery/pkg/networking/grpcgen/cds.go
index 320a8f04..001ed783 100644
--- a/dubbod/discovery/pkg/networking/grpcgen/cds.go
+++ b/dubbod/discovery/pkg/networking/grpcgen/cds.go
@@ -167,6 +167,12 @@ func (b *clusterBuilder) build() []*cluster.Cluster {
if newDefaultCluster != nil {
defaultCluster = newDefaultCluster
}
+ if b.node != nil && b.node.IsRouter() {
+ b.applyBackendTLSPolicy(defaultCluster)
+ for _, subsetCluster := range subsetClusters {
+ b.applyBackendTLSPolicy(subsetCluster)
+ }
+ }
out := make([]*cluster.Cluster, 0, 1+len(subsetClusters))
if defaultCluster != nil {
out = append(out, defaultCluster)
@@ -391,6 +397,28 @@ func (b *clusterBuilder) applyTLSForCluster(c
*cluster.Cluster, subset *networki
log.Debugf("applied %v TLS transport socket to cluster %s (SNI=%s)",
mode, c.Name, sni)
}
+func (b *clusterBuilder) applyBackendTLSPolicy(c *cluster.Cluster) {
+ if c == nil || c.TransportSocket != nil || b.svc == nil || b.push ==
nil {
+ return
+ }
+ if b.svc.Resolution != model.Alias ||
b.svc.Attributes.K8sAttributes.ExternalName == "" {
+ return
+ }
+ settings, found :=
b.push.BackendTLSForService(b.svc.Attributes.Namespace, b.svc.Attributes.Name)
+ if !found || settings.SNI == "" {
+ return
+ }
+ tlsContext := &tlsv1.UpstreamTlsContext{
+ CommonTlsContext: &tlsv1.CommonTlsContext{},
+ Sni: settings.SNI,
+ }
+ c.TransportSocket = &core.TransportSocket{
+ Name: "transport_sockets.tls",
+ ConfigType: &core.TransportSocket_TypedConfig{TypedConfig:
protoconv.MessageToAny(tlsContext)},
+ }
+ log.Debugf("applied BackendTLSPolicy simple TLS transport socket to
cluster %s (SNI=%s)", c.Name, settings.SNI)
+}
+
// buildUpstreamTLSContext builds an UpstreamTlsContext that conforms to gRPC
xDS expectations,
// reusing the common certificate-provider setup from buildCommonTLSContext.
func (b *clusterBuilder) buildUpstreamTLSContext(c *cluster.Cluster,
tlsSettings *networking.ClientTLSSettings) *tlsv1.UpstreamTlsContext {
diff --git a/dubbod/discovery/pkg/networking/grpcgen/cds_test.go
b/dubbod/discovery/pkg/networking/grpcgen/cds_test.go
new file mode 100644
index 00000000..996b5398
--- /dev/null
+++ b/dubbod/discovery/pkg/networking/grpcgen/cds_test.go
@@ -0,0 +1,91 @@
+// 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
+// (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.
+
+package grpcgen
+
+import (
+ "testing"
+ "time"
+
+ "github.com/apache/dubbo-kubernetes/dubbod/discovery/pkg/model"
+ "github.com/apache/dubbo-kubernetes/pkg/config"
+ "github.com/apache/dubbo-kubernetes/pkg/config/schema/gvk"
+ cluster "github.com/kdubbo/xds-api/cluster/v1"
+ tlsv1 "github.com/kdubbo/xds-api/extensions/transport_sockets/tls/v1"
+ gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+func TestBuildClustersExternalNameBackendTLSPolicyUsesSimpleTLS(t *testing.T) {
+ hostName := "httpbin-egress.app.svc.cluster.local"
+ service := newRDSTestService("httpbin-egress", "app", hostName, 443)
+ service.Resolution = model.Alias
+ service.Attributes.K8sAttributes.ExternalName = "httpbin.org"
+ push := newRDSTestPushContext(t, []config.Config{
+ newBackendTLSPolicyConfig("httpbin-tls", "app",
"httpbin-egress", "httpbin.org"),
+ }, []*model.Service{service})
+
+ resources := (&GrpcConfigGenerator{}).BuildClusters(&model.Proxy{
+ ID: "router~10.0.0.1~dxgate.app~app.svc.cluster.local",
+ Type: model.Router,
+ }, push, []string{"outbound|443||" + hostName})
+
+ if len(resources) != 1 {
+ t.Fatalf("resources = %d, want 1", len(resources))
+ }
+ c := &cluster.Cluster{}
+ if err := resources[0].GetResource().UnmarshalTo(c); err != nil {
+ t.Fatalf("unmarshal cluster: %v", err)
+ }
+ if c.GetTransportSocket() == nil {
+ t.Fatal("transport socket is nil")
+ }
+ tlsContext := &tlsv1.UpstreamTlsContext{}
+ if err :=
c.GetTransportSocket().GetTypedConfig().UnmarshalTo(tlsContext); err != nil {
+ t.Fatalf("unmarshal upstream tls context: %v", err)
+ }
+ if tlsContext.GetSni() != "httpbin.org" {
+ t.Fatalf("SNI = %q, want httpbin.org", tlsContext.GetSni())
+ }
+ if
tlsContext.GetCommonTlsContext().GetTlsCertificateCertificateProviderInstance()
!= nil {
+ t.Fatal("simple TLS should not configure a workload certificate
provider")
+ }
+}
+
+func newBackendTLSPolicyConfig(name, namespace, serviceName, hostname string)
config.Config {
+ wellKnown := gatewayv1.WellKnownCACertificatesSystem
+ return config.Config{
+ Meta: config.Meta{
+ GroupVersionKind: gvk.BackendTLSPolicy,
+ Name: name,
+ Namespace: namespace,
+ CreationTimestamp: time.Unix(1, 0),
+ },
+ Spec: &gatewayv1.BackendTLSPolicySpec{
+ TargetRefs:
[]gatewayv1.LocalPolicyTargetReferenceWithSectionName{
+ {
+ LocalPolicyTargetReference:
gatewayv1.LocalPolicyTargetReference{
+ Group: gatewayv1.Group(""),
+ Kind:
gatewayv1.Kind("Service"),
+ Name:
gatewayv1.ObjectName(serviceName),
+ },
+ },
+ },
+ Validation: gatewayv1.BackendTLSPolicyValidation{
+ WellKnownCACertificates: &wellKnown,
+ Hostname:
gatewayv1.PreciseHostname(hostname),
+ },
+ },
+ }
+}
diff --git a/dubbod/discovery/pkg/xds/endpoints/endpoint_builder.go
b/dubbod/discovery/pkg/xds/endpoints/endpoint_builder.go
index a6d78d2c..6ba24d46 100644
--- a/dubbod/discovery/pkg/xds/endpoints/endpoint_builder.go
+++ b/dubbod/discovery/pkg/xds/endpoints/endpoint_builder.go
@@ -96,6 +96,9 @@ func (b *EndpointBuilder)
BuildClusterLoadAssignmentWithGateways(endpointIndex *
if svcPort == nil {
return buildEmptyClusterLoadAssignment(b.clusterName)
}
+ if externalName := b.service.Attributes.K8sAttributes.ExternalName;
externalName != "" {
+ return buildDNSClusterLoadAssignment(b.clusterName,
externalName, uint32(svcPort.Port))
+ }
shards, ok := endpointIndex.ShardsForService(string(b.hostname),
b.service.Attributes.Namespace)
if !ok {
@@ -367,6 +370,33 @@ func buildEmptyClusterLoadAssignment(clusterName string)
*endpoint.ClusterLoadAs
}
}
+func buildDNSClusterLoadAssignment(clusterName, address string, port uint32)
*endpoint.ClusterLoadAssignment {
+ return &endpoint.ClusterLoadAssignment{
+ ClusterName: clusterName,
+ Endpoints: []*endpoint.LocalityLbEndpoints{
+ {
+ Locality: &core.Locality{},
+ LbEndpoints: []*endpoint.LbEndpoint{
+ {
+ HostIdentifier:
&endpoint.LbEndpoint_Endpoint{
+ Endpoint:
&endpoint.Endpoint{
+ Address:
util.BuildAddress(address, port),
+ },
+ },
+ HealthStatus:
core.HealthStatus_HEALTHY,
+ LoadBalancingWeight:
&wrapperspb.UInt32Value{
+ Value: 1,
+ },
+ },
+ },
+ LoadBalancingWeight: &wrapperspb.UInt32Value{
+ Value: 1,
+ },
+ },
+ },
+ }
+}
+
// Cacheable implements model.XdsCacheEntry
func (b *EndpointBuilder) Cacheable() bool {
return b.service != nil
diff --git a/dubbod/discovery/pkg/xds/endpoints/endpoint_builder_test.go
b/dubbod/discovery/pkg/xds/endpoints/endpoint_builder_test.go
index 40677091..fff6528c 100644
--- a/dubbod/discovery/pkg/xds/endpoints/endpoint_builder_test.go
+++ b/dubbod/discovery/pkg/xds/endpoints/endpoint_builder_test.go
@@ -38,6 +38,26 @@ func
TestBuildClusterLoadAssignmentKeepsAppPortWithoutDUBBOMutual(t *testing.T)
}
}
+func TestBuildClusterLoadAssignmentUsesExternalNameDNS(t *testing.T) {
+ hostname := host.Name("httpbin-egress.app.svc.cluster.local")
+ svc := newEndpointTestService("httpbin-egress", "app",
string(hostname), 443)
+ svc.Resolution = model.Alias
+ svc.Attributes.K8sAttributes.ExternalName = "httpbin.org"
+ push := newEndpointTestPushContext(t, nil, []*model.Service{svc})
+ index := model.NewEndpointIndex(model.DisabledCache{})
+
+ clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "",
hostname, 443)
+ builder := NewEndpointBuilder(clusterName, newEndpointTestProxy(), push)
+ cla := builder.BuildClusterLoadAssignment(index)
+
+ if got := firstEndpointAddress(t, cla); got != "httpbin.org" {
+ t.Fatalf("endpoint address = %q, want external DNS name", got)
+ }
+ if got := firstEndpointPort(t, cla); got != 443 {
+ t.Fatalf("endpoint port = %d, want service port 443", got)
+ }
+}
+
func TestBuildClusterLoadAssignmentUsesEastWestGatewayForRemoteShard(t
*testing.T) {
hostname := host.Name("nginx.app.svc.cluster.local")
svc := newEndpointTestService("nginx", "app", string(hostname), 80)
diff --git a/manifests/charts/dubbod/files/kube-gateway.yaml
b/manifests/charts/dubbod/files/kube-gateway.yaml
index de2e38b8..d0962904 100644
--- a/manifests/charts/dubbod/files/kube-gateway.yaml
+++ b/manifests/charts/dubbod/files/kube-gateway.yaml
@@ -68,6 +68,9 @@ spec:
dubbo.apache.org/rev: {{ .Revision | quote }}
gateway.dubbo.apache.org/bootstrap-hash: {{ .BootstrapConfigHash |
quote }}
inject.dubbo.apache.org/templates: grpc-engine
+ prometheus.io/scrape: "true"
+ prometheus.io/path: /metrics
+ prometheus.io/port: "26021"
spec:
serviceAccountName: {{ .ServiceAccount }}
containers:
@@ -81,6 +84,10 @@ spec:
value: 0.0.0.0:8080
- name: DXGATE_ADMIN_ADDR
value: 0.0.0.0:26021
+{{- if .OtelEndpoint }}
+ - name: DXGATE_OTEL_ENDPOINT
+ value: {{ .OtelEndpoint | quote }}
+{{- end }}
- name: POD_NAME
valueFrom:
fieldRef:
diff --git a/pkg/config/schema/collections/collections.gen.go
b/pkg/config/schema/collections/collections.gen.go
index 60ace61c..c454cc1a 100755
--- a/pkg/config/schema/collections/collections.gen.go
+++ b/pkg/config/schema/collections/collections.gen.go
@@ -42,6 +42,24 @@ var (
ValidateProto: validation.EmptyValidate,
}.MustBuild()
+ BackendTLSPolicy = resource.Builder{
+ Identifier: "BackendTLSPolicy",
+ Group: "gateway.networking.k8s.io",
+ Kind: "BackendTLSPolicy",
+ Plural: "backendtlspolicies",
+ Version: "v1",
+ VersionAliases: []string{
+ "v1",
+ },
+ Proto: "k8s.io.gateway_api.api.v1alpha1.BackendTLSPolicySpec",
StatusProto: "k8s.io.gateway_api.api.v1alpha1.PolicyStatus",
+ ReflectType:
reflect.TypeOf(&sigsk8siogatewayapiapisv1.BackendTLSPolicySpec{}).Elem(),
StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.PolicyStatus{}).Elem(),
+ ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1", StatusPackage:
"sigs.k8s.io/gateway-api/apis/v1",
+ ClusterScoped: false,
+ Synthetic: false,
+ Builtin: false,
+ ValidateProto: validation.EmptyValidate,
+ }.MustBuild()
+
CircuitBreakerPolicy = resource.Builder{
Identifier: "CircuitBreakerPolicy",
Group: "networking.dubbo.apache.org",
@@ -429,6 +447,7 @@ var (
// All contains all collections in the system.
All = collection.NewSchemasBuilder().
MustAdd(AuthorizationPolicy).
+ MustAdd(BackendTLSPolicy).
MustAdd(CircuitBreakerPolicy).
MustAdd(ConfigMap).
MustAdd(CustomResourceDefinition).
@@ -458,6 +477,7 @@ var (
// Kube contains only kubernetes collections.
Kube = collection.NewSchemasBuilder().
+ MustAdd(BackendTLSPolicy).
MustAdd(ConfigMap).
MustAdd(CustomResourceDefinition).
MustAdd(DaemonSet).
@@ -492,6 +512,7 @@ var (
// dubboGatewayAPI contains only collections used by Dubbo, including
the full Gateway API.
dubboGatewayAPI = collection.NewSchemasBuilder().
MustAdd(AuthorizationPolicy).
+ MustAdd(BackendTLSPolicy).
MustAdd(CircuitBreakerPolicy).
MustAdd(GatewayClass).
MustAdd(HTTPRoute).
@@ -503,6 +524,7 @@ var (
// dubboStableGatewayAPI contains only collections used by Dubbo,
including beta+ Gateway API.
dubboStableGatewayAPI = collection.NewSchemasBuilder().
MustAdd(AuthorizationPolicy).
+ MustAdd(BackendTLSPolicy).
MustAdd(CircuitBreakerPolicy).
MustAdd(GatewayClass).
MustAdd(HTTPRoute).
diff --git a/pkg/config/schema/gvk/resources.gen.go
b/pkg/config/schema/gvk/resources.gen.go
index f1540b62..5e3768ba 100755
--- a/pkg/config/schema/gvk/resources.gen.go
+++ b/pkg/config/schema/gvk/resources.gen.go
@@ -11,6 +11,8 @@ import (
var (
AuthorizationPolicy = config.GroupVersionKind{Group:
"security.dubbo.apache.org", Version: "v1alpha3", Kind: "AuthorizationPolicy"}
+ BackendTLSPolicy = config.GroupVersionKind{Group:
"gateway.networking.k8s.io", Version: "v1", Kind: "BackendTLSPolicy"}
+ BackendTLSPolicy_v1 = config.GroupVersionKind{Group:
"gateway.networking.k8s.io", Version: "v1", Kind: "BackendTLSPolicy"}
CircuitBreakerPolicy = config.GroupVersionKind{Group:
"networking.dubbo.apache.org", Version: "v1alpha3", Kind:
"CircuitBreakerPolicy"}
ConfigMap = config.GroupVersionKind{Group: "",
Version: "v1", Kind: "ConfigMap"}
CustomResourceDefinition = config.GroupVersionKind{Group:
"apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
@@ -46,6 +48,10 @@ func ToGVR(g config.GroupVersionKind)
(schema.GroupVersionResource, bool) {
switch g {
case AuthorizationPolicy:
return gvr.AuthorizationPolicy, true
+ case BackendTLSPolicy:
+ return gvr.BackendTLSPolicy, true
+ case BackendTLSPolicy_v1:
+ return gvr.BackendTLSPolicy_v1, true
case CircuitBreakerPolicy:
return gvr.CircuitBreakerPolicy, true
case ConfigMap:
@@ -111,6 +117,8 @@ func MustToKind(g config.GroupVersionKind) kind.Kind {
switch g {
case AuthorizationPolicy:
return kind.AuthorizationPolicy
+ case BackendTLSPolicy:
+ return kind.BackendTLSPolicy
case CircuitBreakerPolicy:
return kind.CircuitBreakerPolicy
case ConfigMap:
@@ -181,6 +189,8 @@ func FromGVR(g schema.GroupVersionResource)
(config.GroupVersionKind, bool) {
switch g {
case gvr.AuthorizationPolicy:
return AuthorizationPolicy, true
+ case gvr.BackendTLSPolicy:
+ return BackendTLSPolicy, true
case gvr.CircuitBreakerPolicy:
return CircuitBreakerPolicy, true
case gvr.ConfigMap:
diff --git a/pkg/config/schema/gvr/resources.gen.go
b/pkg/config/schema/gvr/resources.gen.go
index 52be18a4..21955199 100755
--- a/pkg/config/schema/gvr/resources.gen.go
+++ b/pkg/config/schema/gvr/resources.gen.go
@@ -6,6 +6,8 @@ import "k8s.io/apimachinery/pkg/runtime/schema"
var (
AuthorizationPolicy = schema.GroupVersionResource{Group:
"security.dubbo.apache.org", Version: "v1alpha3", Resource:
"authorizationpolicies"}
+ BackendTLSPolicy = schema.GroupVersionResource{Group:
"gateway.networking.k8s.io", Version: "v1", Resource: "backendtlspolicies"}
+ BackendTLSPolicy_v1 = schema.GroupVersionResource{Group:
"gateway.networking.k8s.io", Version: "v1", Resource: "backendtlspolicies"}
CircuitBreakerPolicy = schema.GroupVersionResource{Group:
"networking.dubbo.apache.org", Version: "v1alpha3", Resource:
"circuitbreakerpolicies"}
ConfigMap = schema.GroupVersionResource{Group: "",
Version: "v1", Resource: "configmaps"}
CustomResourceDefinition = schema.GroupVersionResource{Group:
"apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"}
@@ -40,6 +42,10 @@ func IsClusterScoped(g schema.GroupVersionResource) bool {
switch g {
case AuthorizationPolicy:
return false
+ case BackendTLSPolicy:
+ return false
+ case BackendTLSPolicy_v1:
+ return false
case CircuitBreakerPolicy:
return false
case ConfigMap:
diff --git a/pkg/config/schema/kind/resources.gen.go
b/pkg/config/schema/kind/resources.gen.go
index f31e2742..40ab69ab 100755
--- a/pkg/config/schema/kind/resources.gen.go
+++ b/pkg/config/schema/kind/resources.gen.go
@@ -6,6 +6,7 @@ const (
Unknown Kind = iota
Address
AuthorizationPolicy
+ BackendTLSPolicy
CircuitBreakerPolicy
ConfigMap
CustomResourceDefinition
@@ -40,6 +41,8 @@ func (k Kind) String() string {
return "Address"
case AuthorizationPolicy:
return "AuthorizationPolicy"
+ case BackendTLSPolicy:
+ return "BackendTLSPolicy"
case CircuitBreakerPolicy:
return "CircuitBreakerPolicy"
case ConfigMap:
@@ -103,6 +106,8 @@ func FromString(s string) Kind {
return Address
case "AuthorizationPolicy":
return AuthorizationPolicy
+ case "BackendTLSPolicy":
+ return BackendTLSPolicy
case "CircuitBreakerPolicy":
return CircuitBreakerPolicy
case "ConfigMap":
diff --git a/pkg/config/schema/kubeclient/resources.gen.go
b/pkg/config/schema/kubeclient/resources.gen.go
index e07946d8..c571b09b 100755
--- a/pkg/config/schema/kubeclient/resources.gen.go
+++ b/pkg/config/schema/kubeclient/resources.gen.go
@@ -34,6 +34,8 @@ func GetWriteClient[T runtime.Object](c ClientGetter,
namespace string) ktypes.W
switch any(ptr.Empty[T]()).(type) {
case
*apigithubcomapachedubbokubernetesapisecurityv1alpha3.AuthorizationPolicy:
return
c.Dubbo().SecurityV1alpha3().AuthorizationPolicies(namespace).(ktypes.WriteAPI[T])
+ case *sigsk8siogatewayapiapisv1.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(namespace).(ktypes.WriteAPI[T])
case
*apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(namespace).(ktypes.WriteAPI[T])
case *k8sioapicorev1.ConfigMap:
@@ -91,6 +93,8 @@ func GetClient[T, TL runtime.Object](c ClientGetter,
namespace string) ktypes.Re
switch any(ptr.Empty[T]()).(type) {
case
*apigithubcomapachedubbokubernetesapisecurityv1alpha3.AuthorizationPolicy:
return
c.Dubbo().SecurityV1alpha3().AuthorizationPolicies(namespace).(ktypes.ReadWriteAPI[T,
TL])
+ case *sigsk8siogatewayapiapisv1.BackendTLSPolicy:
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(namespace).(ktypes.ReadWriteAPI[T,
TL])
case
*apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy:
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(namespace).(ktypes.ReadWriteAPI[T,
TL])
case *k8sioapicorev1.ConfigMap:
@@ -148,6 +152,8 @@ func gvrToObject(g schema.GroupVersionResource)
runtime.Object {
switch g {
case gvr.AuthorizationPolicy:
return
&apigithubcomapachedubbokubernetesapisecurityv1alpha3.AuthorizationPolicy{}
+ case gvr.BackendTLSPolicy:
+ return &sigsk8siogatewayapiapisv1.BackendTLSPolicy{}
case gvr.CircuitBreakerPolicy:
return
&apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy{}
case gvr.ConfigMap:
@@ -213,6 +219,13 @@ func getInformerFiltered(c ClientGetter, opts
ktypes.InformerOptions, g schema.G
w = func(options metav1.ListOptions) (watch.Interface, error) {
return
c.Dubbo().SecurityV1alpha3().AuthorizationPolicies(opts.Namespace).Watch(context.Background(),
options)
}
+ case gvr.BackendTLSPolicy:
+ l = func(options metav1.ListOptions) (runtime.Object, error) {
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(opts.Namespace).List(context.Background(),
options)
+ }
+ w = func(options metav1.ListOptions) (watch.Interface, error) {
+ return
c.GatewayAPI().GatewayV1().BackendTLSPolicies(opts.Namespace).Watch(context.Background(),
options)
+ }
case gvr.CircuitBreakerPolicy:
l = func(options metav1.ListOptions) (runtime.Object, error) {
return
c.Dubbo().NetworkingV1alpha3().CircuitBreakerPolicies(opts.Namespace).List(context.Background(),
options)
diff --git a/pkg/config/schema/kubetypes/resources.gen.go
b/pkg/config/schema/kubetypes/resources.gen.go
index 910b664f..38722483 100755
--- a/pkg/config/schema/kubetypes/resources.gen.go
+++ b/pkg/config/schema/kubetypes/resources.gen.go
@@ -27,6 +27,8 @@ func getGvk(obj any) (config.GroupVersionKind, bool) {
return gvk.AuthorizationPolicy, true
case
*apigithubcomapachedubbokubernetesapisecurityv1alpha3.AuthorizationPolicy:
return gvk.AuthorizationPolicy, true
+ case *sigsk8siogatewayapiapisv1.BackendTLSPolicy:
+ return gvk.BackendTLSPolicy, true
case *githubcomkdubboapinetworkingv1alpha3.CircuitBreakerPolicy:
return gvk.CircuitBreakerPolicy, true
case
*apigithubcomapachedubbokubernetesapinetworkingv1alpha3.CircuitBreakerPolicy:
diff --git a/pkg/config/schema/metadata.yaml b/pkg/config/schema/metadata.yaml
index c62921b0..5359af4a 100644
--- a/pkg/config/schema/metadata.yaml
+++ b/pkg/config/schema/metadata.yaml
@@ -191,6 +191,17 @@ resources:
statusProto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteStatus"
statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1"
+ - kind: "BackendTLSPolicy"
+ plural: "backendtlspolicies"
+ group: "gateway.networking.k8s.io"
+ version: "v1"
+ versionAliases:
+ - "v1"
+ protoPackage: "sigs.k8s.io/gateway-api/apis/v1"
+ proto: "k8s.io.gateway_api.api.v1alpha1.BackendTLSPolicySpec"
+ statusProto: "k8s.io.gateway_api.api.v1alpha1.PolicyStatus"
+ statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1"
+
## Dubbo resources
- kind: AuthorizationPolicy
plural: "authorizationpolicies"
diff --git a/samples/addons/grafana.yaml b/samples/addons/grafana.yaml
index 98651b8c..406eebd8 100644
--- a/samples/addons/grafana.yaml
+++ b/samples/addons/grafana.yaml
@@ -804,6 +804,163 @@ data:
],
"title": "XDS Reject Rate",
"type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 40
+ },
+ "id": 19,
+ "panels": [],
+ "title": "Gateway Traffic",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "lineWidth": 1,
+ "showPoints": "never"
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 0,
+ "y": 41
+ },
+ "id": 20,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "expr": "sum by (route, cluster)
(rate(dxgate_http_route_requests_total[5m]))",
+ "legendFormat": "{{route}} / {{cluster}}",
+ "refId": "A"
+ }
+ ],
+ "title": "HTTP Request Rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "lineWidth": 1,
+ "showPoints": "never"
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 8,
+ "y": 41
+ },
+ "id": 21,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "expr": "sum by (route, cluster)
(rate(dxgate_http_route_failures_total[5m]))",
+ "legendFormat": "{{route}} / {{cluster}}",
+ "refId": "A"
+ }
+ ],
+ "title": "HTTP Failure Rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "lineWidth": 1,
+ "showPoints": "never"
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 16,
+ "y": 41
+ },
+ "id": 22,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "expr": "histogram_quantile(0.95, sum by (le, route, cluster)
(rate(dxgate_http_route_latency_ms_bucket[5m])))",
+ "legendFormat": "{{route}} / {{cluster}}",
+ "refId": "A"
+ }
+ ],
+ "title": "HTTP Upstream P95",
+ "type": "timeseries"
}
],
"refresh": "15s",
diff --git a/samples/addons/prometheus.yaml b/samples/addons/prometheus.yaml
index 7d46e93f..17f1d459 100644
--- a/samples/addons/prometheus.yaml
+++ b/samples/addons/prometheus.yaml
@@ -22,6 +22,34 @@ metadata:
app.kubernetes.io/name: prometheus
app.kubernetes.io/part-of: dubbo-observability
---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: dubbo-prometheus
+ labels:
+ app.kubernetes.io/name: prometheus
+ app.kubernetes.io/part-of: dubbo-observability
+rules:
+- apiGroups: [""]
+ resources: ["nodes", "pods", "services", "endpoints"]
+ verbs: ["get", "list", "watch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: dubbo-prometheus
+ labels:
+ app.kubernetes.io/name: prometheus
+ app.kubernetes.io/part-of: dubbo-observability
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: dubbo-prometheus
+subjects:
+- kind: ServiceAccount
+ name: prometheus
+ namespace: dubbo-system
+---
apiVersion: v1
kind: ConfigMap
metadata:
@@ -50,6 +78,29 @@ data:
labels:
app: dubbod
namespace: dubbo-system
+
+ - job_name: kubernetes-pods
+ kubernetes_sd_configs:
+ - role: pod
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
+ action: keep
+ regex: "true"
+ - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
+ action: replace
+ target_label: __metrics_path__
+ regex: (.+)
+ - source_labels: [__address__,
__meta_kubernetes_pod_annotation_prometheus_io_port]
+ action: replace
+ regex: ([^:]+)(?::\d+)?;(\d+)
+ replacement: $1:$2
+ target_label: __address__
+ - source_labels: [__meta_kubernetes_namespace]
+ target_label: namespace
+ - source_labels: [__meta_kubernetes_pod_name]
+ target_label: pod
+ - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
+ target_label: app
---
apiVersion: v1
kind: Service
diff --git a/samples/addons/prometheus.yaml b/samples/addons/tracing.yaml
similarity index 50%
copy from samples/addons/prometheus.yaml
copy to samples/addons/tracing.yaml
index 7d46e93f..f63e7ced 100644
--- a/samples/addons/prometheus.yaml
+++ b/samples/addons/tracing.yaml
@@ -16,117 +16,91 @@
apiVersion: v1
kind: ServiceAccount
metadata:
- name: prometheus
+ name: tracing
namespace: dubbo-system
labels:
- app.kubernetes.io/name: prometheus
+ app.kubernetes.io/name: tracing
app.kubernetes.io/part-of: dubbo-observability
---
apiVersion: v1
-kind: ConfigMap
-metadata:
- name: prometheus
- namespace: dubbo-system
- labels:
- app.kubernetes.io/name: prometheus
- app.kubernetes.io/part-of: dubbo-observability
-data:
- prometheus.yml: |
- global:
- scrape_interval: 15s
- evaluation_interval: 15s
-
- scrape_configs:
- - job_name: prometheus
- static_configs:
- - targets:
- - localhost:9090
-
- - job_name: dubbod
- metrics_path: /metrics
- static_configs:
- - targets:
- - dubbod.dubbo-system.svc:8080
- labels:
- app: dubbod
- namespace: dubbo-system
----
-apiVersion: v1
kind: Service
metadata:
- name: prometheus
+ name: tracing
namespace: dubbo-system
labels:
- app.kubernetes.io/name: prometheus
+ app.kubernetes.io/name: tracing
app.kubernetes.io/part-of: dubbo-observability
spec:
+ selector:
+ app.kubernetes.io/name: tracing
ports:
- - name: http
- port: 9090
+ - name: http-query
+ port: 16686
+ targetPort: http-query
+ protocol: TCP
+ - name: grpc-otlp
+ port: 4317
+ targetPort: grpc-otlp
+ protocol: TCP
+ - name: http-otlp
+ port: 4318
+ targetPort: http-otlp
protocol: TCP
- targetPort: 9090
- selector:
- app.kubernetes.io/name: prometheus
---
apiVersion: apps/v1
kind: Deployment
metadata:
- name: prometheus
+ name: tracing
namespace: dubbo-system
labels:
- app.kubernetes.io/name: prometheus
+ app.kubernetes.io/name: tracing
app.kubernetes.io/part-of: dubbo-observability
spec:
replicas: 1
selector:
matchLabels:
- app.kubernetes.io/name: prometheus
+ app.kubernetes.io/name: tracing
template:
metadata:
labels:
- app.kubernetes.io/name: prometheus
+ app.kubernetes.io/name: tracing
app.kubernetes.io/part-of: dubbo-observability
spec:
- serviceAccountName: prometheus
+ serviceAccountName: tracing
containers:
- - name: prometheus
- image: prom/prometheus:v3.5.0
+ - name: jaeger
+ image: docker.io/jaegertracing/all-in-one:1.60
imagePullPolicy: IfNotPresent
- args:
- - --config.file=/etc/prometheus/prometheus.yml
- - --storage.tsdb.path=/prometheus
- - --web.enable-lifecycle
+ env:
+ - name: COLLECTOR_OTLP_ENABLED
+ value: "true"
+ - name: QUERY_BASE_PATH
+ value: /
ports:
- - name: http
- containerPort: 9090
+ - name: http-query
+ containerPort: 16686
+ protocol: TCP
+ - name: grpc-otlp
+ containerPort: 4317
+ protocol: TCP
+ - name: http-otlp
+ containerPort: 4318
protocol: TCP
readinessProbe:
httpGet:
- path: /-/ready
- port: 9090
+ path: /
+ port: http-query
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
- path: /-/healthy
- port: 9090
- initialDelaySeconds: 30
+ path: /
+ port: http-query
+ initialDelaySeconds: 20
periodSeconds: 15
resources:
requests:
cpu: 100m
- memory: 256Mi
+ memory: 128Mi
limits:
memory: 512Mi
- volumeMounts:
- - name: config
- mountPath: /etc/prometheus
- readOnly: true
- - name: storage
- mountPath: /prometheus
- volumes:
- - name: config
- configMap:
- name: prometheus
- - name: storage
- emptyDir: {}