From 5e36fa4bc3e120927580872cf0262ed48fe6237e Mon Sep 17 00:00:00 2001 From: raaizik Date: Wed, 22 Jul 2026 14:59:22 +0300 Subject: [PATCH] nfs: support custom NFS server port When NFS runs with host networking, port 2049 may already be in use. Add CephNFS spec.server.port (default 2049), wire it through Ganesha config, the operator Service, and probes, and regenerate CRDs. Signed-off-by: raaizik --- Documentation/CRDs/specification.md | 13 +++++++++++++ .../charts/rook-ceph/templates/resources.yaml | 8 ++++++++ deploy/examples/crds.yaml | 8 ++++++++ pkg/apis/ceph.rook.io/v1/nfs.go | 11 +++++++++++ pkg/apis/ceph.rook.io/v1/types.go | 7 +++++++ pkg/operator/ceph/nfs/config.go | 2 ++ pkg/operator/ceph/nfs/spec.go | 18 ++++++++---------- 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Documentation/CRDs/specification.md b/Documentation/CRDs/specification.md index d0135b67f..4dc23c711 100644 --- a/Documentation/CRDs/specification.md +++ b/Documentation/CRDs/specification.md @@ -8259,6 +8259,19 @@ bool +port
+ +int32 + + + +(Optional) +

The port the NFS server (NFS-Ganesha) will listen on for NFS clients. Defaults to 2049. +Useful when host networking is enabled and the default NFS port is already in use.

+ + + + livenessProbe
diff --git a/deploy/charts/rook-ceph/templates/resources.yaml b/deploy/charts/rook-ceph/templates/resources.yaml index 423d3daf5..6c3487827 100644 --- a/deploy/charts/rook-ceph/templates/resources.yaml +++ b/deploy/charts/rook-ceph/templates/resources.yaml @@ -11515,6 +11515,14 @@ spec: type: array type: object x-kubernetes-preserve-unknown-fields: true + port: + description: |- + The port the NFS server (NFS-Ganesha) will listen on for NFS clients. Defaults to 2049. + Useful when host networking is enabled and the default NFS port is already in use. + format: int32 + maximum: 65535 + minimum: 1 + type: integer priorityClassName: description: PriorityClassName sets the priority class on the pods type: string diff --git a/deploy/examples/crds.yaml b/deploy/examples/crds.yaml index 316291836..6bdfb9d69 100644 --- a/deploy/examples/crds.yaml +++ b/deploy/examples/crds.yaml @@ -11508,6 +11508,14 @@ spec: type: array type: object x-kubernetes-preserve-unknown-fields: true + port: + description: |- + The port the NFS server (NFS-Ganesha) will listen on for NFS clients. Defaults to 2049. + Useful when host networking is enabled and the default NFS port is already in use. + format: int32 + maximum: 65535 + minimum: 1 + type: integer priorityClassName: description: PriorityClassName sets the priority class on the pods type: string diff --git a/pkg/apis/ceph.rook.io/v1/nfs.go b/pkg/apis/ceph.rook.io/v1/nfs.go index 37d5a3cf7..469d37c85 100644 --- a/pkg/apis/ceph.rook.io/v1/nfs.go +++ b/pkg/apis/ceph.rook.io/v1/nfs.go @@ -49,6 +49,17 @@ func (n *CephNFS) IsHostNetwork(c *ClusterSpec) bool { return c.Network.IsHost() } +// DefaultNFSPort is the default port NFS-Ganesha listens on for NFS clients. +const DefaultNFSPort int32 = 2049 + +// GetPort returns the NFS listen port, or the default (2049) if unset. +func (n *CephNFS) GetPort() int32 { + if n.Spec.Server.Port != 0 { + return n.Spec.Server.Port + } + return DefaultNFSPort +} + func (sec *NFSSecuritySpec) Validate() error { if sec == nil { return nil diff --git a/pkg/apis/ceph.rook.io/v1/types.go b/pkg/apis/ceph.rook.io/v1/types.go index 18c802758..0dfe7fb73 100755 --- a/pkg/apis/ceph.rook.io/v1/types.go +++ b/pkg/apis/ceph.rook.io/v1/types.go @@ -3070,6 +3070,13 @@ type GaneshaServerSpec struct { // +optional HostNetwork *bool `json:"hostNetwork,omitempty"` + // The port the NFS server (NFS-Ganesha) will listen on for NFS clients. Defaults to 2049. + // Useful when host networking is enabled and the default NFS port is already in use. + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 + // +optional + Port int32 `json:"port,omitempty"` + // A liveness-probe to verify that Ganesha server has valid run-time state. // If LivenessProbe.Disabled is false and LivenessProbe.Probe is nil uses default probe. // +optional diff --git a/pkg/operator/ceph/nfs/config.go b/pkg/operator/ceph/nfs/config.go index af7bd8da0..e98b34dca 100644 --- a/pkg/operator/ceph/nfs/config.go +++ b/pkg/operator/ceph/nfs/config.go @@ -109,12 +109,14 @@ func getGaneshaConfig(n *cephv1.CephNFS, version cephver.CephVersion, name strin nodeID := getNFSNodeID(n, name) userID := getNFSUserID(nodeID) url := getRadosURL(n) + port := n.GetPort() return ` NFS_CORE_PARAM { Enable_NLM = false; Enable_RQUOTA = false; Protocols = 4; allow_set_io_flusher_fail = true; + NFS_Port = ` + fmt.Sprintf("%d", port) + `; } MDCACHE { diff --git a/pkg/operator/ceph/nfs/spec.go b/pkg/operator/ceph/nfs/spec.go index 5a593c592..d9492936e 100644 --- a/pkg/operator/ceph/nfs/spec.go +++ b/pkg/operator/ceph/nfs/spec.go @@ -30,7 +30,6 @@ import ( "github.com/rook/rook/pkg/util/log" apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -41,13 +40,13 @@ const ( AppName = "rook-ceph-nfs" ganeshaContainerName = "nfs-ganesha" ganeshaConfigVolume = "ganesha-config" - nfsPort = 2049 ganeshaPid = "/var/run/ganesha/ganesha.pid" nfsGaneshaMetricsPort = 9587 ) func (r *ReconcileCephNFS) generateCephNFSService(nfs *cephv1.CephNFS, cfg daemonConfig) *v1.Service { labels := getLabels(nfs, cfg.ID, true) + nfsPort := nfs.GetPort() svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -92,16 +91,13 @@ func (r *ReconcileCephNFS) createCephNFSService(nfs *cephv1.CephNFS, cfg daemonC return errors.Wrapf(err, "failed to set owner reference to ceph nfs %q", s) } - svc, err := r.context.Clientset.CoreV1().Services(nfs.Namespace).Create(r.opManagerContext, s, metav1.CreateOptions{}) + // Update the svc if it already exists so a changed spec.server.port takes effect. + svc, err := k8sutil.CreateOrUpdateService(r.opManagerContext, r.context.Clientset, nfs.Namespace, s) if err != nil { - if !kerrors.IsAlreadyExists(err) { - return errors.Wrap(err, "failed to create ganesha service") - } - log.NamedInfo(nsName, logger, "ceph nfs service already created") - return nil + return errors.Wrap(err, "failed to create or update ganesha service") } - log.NamedInfo(nsName, logger, "ceph nfs service running at %s:%d", svc.Spec.ClusterIP, nfsPort) + log.NamedInfo(nsName, logger, "ceph nfs service running at %s:%d", svc.Spec.ClusterIP, nfs.GetPort()) return nil } @@ -262,10 +258,12 @@ func (r *ReconcileCephNFS) daemonContainer(nfs *cephv1.CephNFS, cfg daemonConfig func (r *ReconcileCephNFS) defaultGaneshaLivenessProbe(nfs *cephv1.CephNFS) *v1.Probe { failureThreshold := int32(10) + nfsPort := nfs.GetPort() cephVersionWithRpcinfo := version.CephVersion{Major: 18, Minor: 2, Extra: 1} if r.clusterInfo.CephVersion.IsAtLeast(cephVersionWithRpcinfo) { // liveness-probe using rpcinfo utility - return controller.GenerateLivenessProbeViaRpcinfo(nfsPort, failureThreshold) + //nolint:gosec // G115: port is CRD-validated to 1-65535 + return controller.GenerateLivenessProbeViaRpcinfo(uint16(nfsPort), failureThreshold) } // liveness-probe using K8s builtin TCP-socket action return controller.GenerateLivenessProbeTcpPort(nfsPort, failureThreshold)