diff --git a/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-service.yml b/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-service.yml index f0747267d..f6859ad90 100644 --- a/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-service.yml +++ b/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-service.yml @@ -73,7 +73,7 @@ spec: targetPort: {{ .Values.allInOne.sftp.port | default .Values.sftp.port }} protocol: TCP {{- end }} - + # Server metrics port (single metrics endpoint for all services) {{- if .Values.allInOne.metricsPort }} - name: "server-metrics" diff --git a/k8s/charts/seaweedfs/templates/volume/volume-ingress.yaml b/k8s/charts/seaweedfs/templates/volume/volume-ingress.yaml index 83ef536cd..b36c29867 100644 --- a/k8s/charts/seaweedfs/templates/volume/volume-ingress.yaml +++ b/k8s/charts/seaweedfs/templates/volume/volume-ingress.yaml @@ -1,4 +1,8 @@ -{{- if and .Values.volume.enabled .Values.volume.ingress.enabled }} +{{- /* Volume ingress works for both normal mode (volume.enabled) and all-in-one mode (allInOne.enabled) */}} +{{- $volumeEnabled := or .Values.volume.enabled .Values.allInOne.enabled }} +{{- if and $volumeEnabled .Values.volume.ingress.enabled }} +{{- /* Determine service name based on deployment mode */}} +{{- $serviceName := ternary (include "seaweedfs.componentName" (list . "all-in-one")) (include "seaweedfs.componentName" (list . "volume")) .Values.allInOne.enabled }} {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1 {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} @@ -42,11 +46,11 @@ spec: backend: {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} service: - name: {{ include "seaweedfs.componentName" (list . "volume") }} + name: {{ $serviceName }} port: number: {{ .Values.volume.port }} {{- else }} - serviceName: {{ include "seaweedfs.componentName" (list . "volume") }} + serviceName: {{ $serviceName }} servicePort: {{ .Values.volume.port }} {{- end }} {{- end }} diff --git a/weed/admin/dash/cluster_topology.go b/weed/admin/dash/cluster_topology.go index aca29cd4a..3611251ff 100644 --- a/weed/admin/dash/cluster_topology.go +++ b/weed/admin/dash/cluster_topology.go @@ -2,13 +2,20 @@ package dash import ( "context" + "encoding/json" "fmt" + "io" + "net/http" "time" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb/master_pb" ) +var dirStatusClient = &http.Client{ + Timeout: 5 * time.Second, +} + // GetClusterTopology returns the current cluster topology with caching func (s *AdminServer) GetClusterTopology() (*ClusterTopology, error) { now := time.Now() @@ -35,8 +42,71 @@ func (s *AdminServer) GetClusterTopology() (*ClusterTopology, error) { return topology, nil } +// fetchPublicUrlMap queries the master's /dir/status HTTP endpoint and returns +// a map from data node ID (ip:port) to its PublicUrl. +func (s *AdminServer) fetchPublicUrlMap() map[string]string { + currentMaster := s.masterClient.GetMaster(context.Background()) + if currentMaster == "" { + return nil + } + + url := fmt.Sprintf("http://%s/dir/status", currentMaster.ToHttpAddress()) + resp, err := dirStatusClient.Get(url) + if err != nil { + glog.V(1).Infof("Failed to fetch /dir/status from %s: %v", currentMaster, err) + return nil + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + glog.V(1).Infof("Non-OK response from /dir/status: %d", resp.StatusCode) + return nil + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + glog.V(1).Infof("Failed to read /dir/status response body: %v", err) + return nil + } + + // Parse the JSON response to extract PublicUrl for each data node + var status struct { + Topology struct { + DataCenters []struct { + Racks []struct { + DataNodes []struct { + Url string `json:"Url"` + PublicUrl string `json:"PublicUrl"` + } `json:"DataNodes"` + } `json:"Racks"` + } `json:"DataCenters"` + } `json:"Topology"` + } + + if err := json.Unmarshal(body, &status); err != nil { + glog.V(1).Infof("Failed to parse /dir/status response: %v", err) + return nil + } + + publicUrls := make(map[string]string) + for _, dc := range status.Topology.DataCenters { + for _, rack := range dc.Racks { + for _, dn := range rack.DataNodes { + if dn.PublicUrl != "" { + publicUrls[dn.Url] = dn.PublicUrl + } + } + } + } + return publicUrls +} + // getTopologyViaGRPC gets topology using gRPC (original method) func (s *AdminServer) getTopologyViaGRPC(topology *ClusterTopology) error { + // Fetch public URL mapping from master HTTP API + // The gRPC DataNodeInfo does not include PublicUrl, so we supplement it. + publicUrls := s.fetchPublicUrlMap() + // Get cluster status from master err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { resp, err := client.VolumeList(context.Background(), &master_pb.VolumeListRequest{}) @@ -85,12 +155,23 @@ func (s *AdminServer) getTopologyViaGRPC(topology *ClusterTopology) error { } } + // Look up PublicUrl from master HTTP API + // Use node.Address (ip:port) as the key, matching the Url field in /dir/status + nodeAddr := node.Address + if nodeAddr == "" { + nodeAddr = node.Id + } + publicUrl := publicUrls[nodeAddr] + if publicUrl == "" { + publicUrl = nodeAddr + } + vs := VolumeServer{ ID: node.Id, Address: node.Id, DataCenter: dc.Id, Rack: rack.Id, - PublicURL: node.Id, + PublicURL: publicUrl, Volumes: int(totalVolumes), MaxVolumes: int(totalMaxVolumes), DiskUsage: totalSize, diff --git a/weed/admin/dash/volume_management.go b/weed/admin/dash/volume_management.go index 805e891d0..67b352733 100644 --- a/weed/admin/dash/volume_management.go +++ b/weed/admin/dash/volume_management.go @@ -413,6 +413,9 @@ func (s *AdminServer) VacuumVolume(volumeID int, server string) error { func (s *AdminServer) GetClusterVolumeServers() (*ClusterVolumeServersData, error) { var volumeServerMap map[string]*VolumeServer + // Fetch public URL mapping from master HTTP API + publicUrls := s.fetchPublicUrlMap() + // Make only ONE VolumeList call and use it for both topology building AND EC shard processing err := s.WithMasterClient(func(client master_pb.SeaweedClient) error { resp, err := client.VolumeList(context.Background(), &master_pb.VolumeListRequest{}) @@ -436,8 +439,18 @@ func (s *AdminServer) GetClusterVolumeServers() (*ClusterVolumeServersData, erro for _, node := range rack.DataNodeInfos { // Initialize volume server if not exists if volumeServerMap[node.Id] == nil { + // Look up PublicUrl from master HTTP API + nodeAddr := node.Address + if nodeAddr == "" { + nodeAddr = node.Id + } + publicUrl := publicUrls[nodeAddr] + if publicUrl == "" { + publicUrl = nodeAddr + } volumeServerMap[node.Id] = &VolumeServer{ Address: node.Id, + PublicURL: publicUrl, DataCenter: dc.Id, Rack: rack.Id, Volumes: 0, diff --git a/weed/server/master_ui/master.html b/weed/server/master_ui/master.html index 40d49991b..ebfeeceb7 100644 --- a/weed/server/master_ui/master.html +++ b/weed/server/master_ui/master.html @@ -88,9 +88,11 @@
{{ $dc.Id }}{{ $dc.Id }}