Minikube NFS mounts
Minikube is great for having a Kubernetes cluster as local Docker development environment. In a development workflow you probably have source code on your host machine and it would be great if the Docker containers in the Kubernetes cluster could mount this. So changes made to the code can be tested and are visible quickly. The steps on this blog post are tested with Minikube version v0.28.0.
Mounting is possible for example with:
And by using hostPath in the volume.
--- apiVersion: apps/v1 kind: Deployment metadata: name: my-api namespace: default labels: app: my-api spec: replicas: 1 selector: matchLabels: app: my-api template: metadata: labels: app: my-api spec: containers: - name: my-api imagePullPolicy: Never image: my-api:v1 ports: - containerPort: 80 volumeMounts: - name: code mountPath: /var/www/app volumes: - name: code hostPath: path: /sources/my-api --- apiVersion: v1 kind: Service metadata: name: my-api namespace: default labels: app: my-api spec: selector: app: my-api ports: - name: app-port port: 80 nodePort: 30101 type: NodePort
By default the VirtualBox VM driver is used. By using the mount above you will get a VirtualBox shared folder. For a project with quite a lot of files, like a PHP framework as Symfony, your load time will be around 20-30 seconds per page request, which is terribly slow.
However NFS mounts can be used and they will give awesome performance! View the comparison. The first page request will load in about five seconds and after that in milliseconds.
NFS server
Configure your sources directory as export (/etc/exports) in the NFS server that runs on your host machine. This way containers can mount source code. This is a lot faster than a default VirtualBox shared folder mount. You only have to do this once, the NFS service will load /etc/exports at (re)boot.
NOTE: The Minikube IP can be different after a minikube delete and minikube start command. Make sure that your NFS export contains the correct Minikube IP again.
Mac OS X
Check if the entry is active by executing on your host machine:
This should output something like:
Exports list on 127.0.0.1: /Absolute/path/to/sources 192.168.99.100
Linux
Similar to the command above for Mac OS X, but the service name can be different depending on the distribution.
Windows
With https://github.com/winnfsd/winnfsd it even works great on Windows! But make sure you configure the NFS version 3 instead of the default version 4.
--- apiVersion: v1 kind: PersistentVolume metadata: name: default-sources-volume spec: capacity: storage: 15Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: standard nfs: # The address 192.168.99.1 is the Minikube gateway to the host. This way # not the container IP will be visible by the NFS server on the host machine, # but the IP address of the `minikube ip` command. You will need to # grant access to the `minikube ip` IP address. server: 192.168.99.1 path: '/C/Users/myname/minikube/sources/default' mountOptions: - nfsvers=3 - udp
UDP protocol because of https://github.com/winnfsd/winnfsd/issues/68. The content of nfs-pathfile.txt. Access is allowed to subdirectories inside this directory.
C:\Users\myname\minikube\sources
The command:
C:\Users\myname\bin\WinNFSd.exe -pathFile "C:\Users\myname\etc\nfs-pathfile.txt"
The Kubernetes and WinNFSd path is case sensitive, make sure it matches!
NFS mounts
The examples in this section are prefixed with “default-“, referring to the default namespace. The examples also work if multiple namespaces are used! Just repeat the steps for each namespace and replace “default” with the proper namespace. Make sure you also change the “namespace” key inside the yaml files.
An important thing to notice is that persistent volumes are global and persistent volume claims live inside a namespace.
Configure a PersistentVolume in a file default-sources-volume.yaml:
--- apiVersion: v1 kind: PersistentVolume metadata: name: default-sources-volume spec: capacity: storage: 15Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: standard nfs: # The address 192.168.99.1 is the Minikube gateway to the host. This way # not the container IP will be visible by the NFS server on the host machine, # but the IP address of the `minikube ip` command. You will need to # grant access to the `minikube ip` IP address. server: 192.168.99.1 path: '/Absolute/path/to/sources/default'
Apply it with:
Configure a PersistentVolumeClaim in a file default-sources-volume-claim.yaml:
--- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: sources-volume-claim namespace: default spec: storageClassName: standard accessModes: - ReadWriteMany resources: requests: storage: 15Gi
Apply it with:
Now you can use the NFS persistent volume to your host machine in your container. Create a file my-api.yaml:
--- apiVersion: apps/v1 kind: Deployment metadata: name: my-api namespace: default labels: app: my-api spec: replicas: 1 selector: matchLabels: app: my-api template: metadata: labels: app: my-api spec: containers: - name: my-api imagePullPolicy: Never image: my-api:v1 ports: - containerPort: 80 volumeMounts: - name: code mountPath: /var/www/app subPath: my-api volumes: - name: code persistentVolumeClaim: claimName: sources-volume-claim --- apiVersion: v1 kind: Service metadata: name: my-api namespace: default labels: app: my-api spec: selector: app: my-api ports: - name: app-port port: 80 nodePort: 30101 type: NodePort
Apply it with:
Now you have awesome fast mounts from your containers to your host machine and a very nice development workflow!
Alternative: NFS mount directly in the VM
Minikube has a sub command “ssh”, this way you can for example build Docker images with the docker client inside the Minikube VM with:
minikube ssh "docker build -t myimage:latest -f /host-sources ."
To create the NFS /host-sources mount:
echo 'Mounting (NFS) /host-sources inside the Minikube VM' sources_dir=$(realpath .)/sources # In case of Windows drive C, uppercase the drive letter so it matches with the WinNFSd exports sources_dir=${sources_dir/\/c\//\/C\/} # The address 192.168.99.1 is the Minikube gateway to the host. NFS version 3 to be compatible with WinNFSd minikube ssh "sudo mkdir -p /host-sources && sudo mount -t nfs -o nfsvers=3,tcp 192.168.99.1:${sources_dir} /host-sources"
Use udp instead of tcp in case of Windows to avoid hanging issues. This NFS mount can also be reused for Docker containers with a hostPath volume mount.
Hey,
I’m trying to get this to work, but I keep getting this error on pod-creation.
MountVolume.SetUp failed for volume “default-sources-volume” : mount failed: exit status 32 Mounting command: systemd-run Mounting arguments: –description=Kubernetes transient mount for /var/lib/kubelet/pods/5de3c8fa-9a28-11e8-8f72-0ad6636f33d3/volumes/kubernetes.io~nfs/default-sources-volume –scope — mount -t nfs 192.168.xx.x:/Users/name/company/projectname /var/lib/kubelet/pods/5de3c8fa-9a28-11e8-8f72-0ad6636f33d3/volumes/kubernetes.io~nfs/default-sources-volume Output: Running scope as unit: run-r5abf7e36dc50424dac44073ddc7e32eb.scope mount.nfs: access denied by server while mounting 192.168.xx.x:/Users/name/company/projectname