This guide will walk you through setting up an NFS server to provide centralized storage for your Kubernetes cluster. We'll use a Docker container as the NFS server and configure it to work seamlessly with Kubernetes. This setup is very useful if you have an on-premise Kubernetes cluster and you're looking for a centralized storage solution.
-
What is NFS?
- NFS (Network File System) allows you to share files between machines over a network. It's commonly used in Kubernetes to provide persistent storage that can be accessed by multiple pods.
-
Why Use This Setup?
- This setup is lightweight, flexible, and easy to manage.
- It works well for small to medium-sized Kubernetes clusters.
-
Requirements
- A Docker host to run the NFS server container.
- A Kubernetes cluster where you want to use the NFS storage.
- Basic knowledge of Docker and Kubernetes.
- Create a separate partition or directory on your Docker host for the shared folder. For example:
mkdir -p /shared chmod 777 /shared chown nobody:nogroup /shared
Tip: Using a separate partition ensures better performance and isolation for your shared storage.
Before starting the service, you will have to enable some kernel features in order for the server run properly.
modprobe nfs
modprobe nfsdWe'll use the erichough/nfs-server image to set up the NFS server. Below is the docker-compose.yml configuration file:
services:
nfs-server:
image: erichough/nfs-server
container_name: nfs-server
hostname: nfs-server
cap_add:
- SYS_ADMIN
restart: always
ports:
# RPC portmapper (required for NFS)
- "111:111" # TCP
- "111:111/udp" # UDP
# NFS service
- "2049:2049" # TCP
- "2049:2049/udp" # UDP
# rpc.statd (status monitor)
- "32765:32765" # TCP
- "32765:32765/udp" # UDP
# rpc.mountd (mount daemon)
- "32767:32767" # TCP
- "32767:32767/udp" # UDP
volumes:
- /shared:/nfsshare # Bind mount for the NFS share
environment:
- NFS_LOG_LEVEL=DEBUG
- NFS_EXPORT_0=/nfsshare *(rw,sync,no_subtree_check,no_root_squash,insecure)
healthcheck:
test: ["CMD", "rpcinfo", "-p", "localhost"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: "1"
memory: "512M"- Ports: These are required for NFS to function. They include:
- Port
111: Used by the RPC portmapper. - Port
2049: The main NFS service port. - Ports
32765and32767: Used by auxiliary services likerpc.statdandrpc.mountd.
- Port
- Volumes: Maps
/sharedon the Docker host to/nfsshareinside the container. This is the directory shared via NFS. - Environment Variables:
NFS_EXPORT_0: Defines the NFS export rules. Replace*with specific client IPs if needed.NFS_LOG_LEVEL: Sets the logging level toDEBUGfor troubleshooting.
Now that the NFS server is running, we need to tell Kubernetes about it by creating a PersistentVolume (PV).
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-server
labels:
# Optional labels
type: nfs
location: city
app: nfs-server
spec:
capacity:
storage: <size-you-want>
accessModes:
- ReadWriteOnce
nfs:
server: <your-server-ip> # Replace with your NFS server IP
path: /nfsshare # Path exported by the NFS server- Replace
<size-you-want>with the actual size of the partition or size you want for your NFS server. - Replace
<your-server-ip>with the actual IP address of your NFS server. - The
pathmust match the directory you mounted in the NFS server container (/nfsshare).
A PersistentVolumeClaim (PVC) is used to request storage from the PV.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
storageClassName: "" # Leave empty if no default StorageClass exists
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi- If your Kubernetes cluster has a default StorageClass, you must explicitly set
storageClassName: ""in the PVC to avoid conflicts. - The
storagevalue (1Gi) should not exceed the capacity defined in the PV.
Finally, attach the PVC to a pod to use the NFS storage.
apiVersion: v1
kind: Pod
metadata:
name: data-consumer
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "3600"]
volumeMounts:
- mountPath: "/mnt/test"
name: test-folder
volumes:
- name: test-folder
persistentVolumeClaim:
claimName: nfs-pvc- The
busyboxcontainer mounts the PVC (nfs-pvc) at/mnt/test. - Any data written to
/mnt/testwill be stored on the NFS server.
Run the following command to check the status of your PV and PVC:
kubectl get pv,pvcYou should see output similar to this:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/nfs-server 100Gi RWO Retain Bound default/nfs-pvc <unset> 113m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/nfs-pvc Bound nfs-server 100Gi RWO <unset> 113m
- Both the PV and PVC should have the status
Bound. - If they are not bound, double-check the NFS server IP, paths, kernel modules, and permissions.
-
Security:
- Restrict NFS access to specific client IPs by modifying the
NFS_EXPORT_0environment variable. - Avoid using
no_root_squashin production environments unless absolutely necessary.
- Restrict NFS access to specific client IPs by modifying the
-
Performance:
- Use a dedicated partition or disk for
/sharedto improve I/O performance. - Monitor resource usage (CPU and memory) of the NFS server container.
- High speed storage is recommended to the get better performance, but it is not mandatory.
- If you use partition you could use RAID to improve speed and Data resilience.
- Use a dedicated partition or disk for
-
Troubleshooting:
- Check the logs of the NFS server container for errors:
docker logs nfs-server
- Check the logs of the pod, pv and pvc:
kubectl describe pod <pod-name> kubectl describe pv <pv-name> kubectl describe pvc <pvc-name>
- Ensure the required kernel modules (
nfs,nfsd, etc.) are loaded on the Docker host.
- Check the logs of the NFS server container for errors:
You now have a fully functional NFS server integrated with your Kubernetes cluster! This setup provides centralized storage that can be shared across multiple pods. Customize the configuration as needed to suit your specific use case.
If you encounter any issues or have questions, feel free to ask!