Kubernetes persistent volumes
Kubernetes persistent volumes
Applications deployed on a Kubernetes cluster run inside containers. As a consequence, their file system is that of the container, which means that if the container is removed, the data it contained is lost.
To save data in a more permanent manner, Kubernetes offers persistent volumes (PV). Those volumes can be in various forms but the one of the simplest consist in mounting a directory of the node's file system into the containers.
A persistent volume achieving such purpose can be created using the following manifest
apiVersion: v1
kind: PersistentVolume
metadata:
name: image-upload-pv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 50Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/kubernetes/image_upload"
Here, /mnt/kubernetes
is the directory that will be mounted in the pods and 50Gi is its maximum allowed size.
Deployments can then claim this volume using a persistent volume claim, or PVC. Here is an example of a deployment that claims the PV defined previously.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: image-upload-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: image-upload-deployment
spec:
replicas: 1
selector:
matchLabels:
app: image-upload-deployment
template:
metadata:
labels:
app: image-upload-deployment
spec:
volumes:
- name: image-upload-storage
persistentVolumeClaim:
claimName: image-upload-pvc
containers:
- name: image-upload-container
image: 192.168.179.25:5000/image-upload
imagePullPolicy: Always
ports:
- containerPort: 8888
volumeMounts:
- mountPath: "/usr/share/pv"
name: image-upload-storage
---
apiVersion: v1
kind: Service
metadata:
labels:
run: image-upload-service
name: image-upload-service
spec:
ports:
- port: 8888
nodePort: 30115
selector:
app: image-upload-deployment
type: LoadBalancer
Note that a PV can be claimed by only one PVC
Here is an example NodeJS app that allows the user to upload files on the PV:
const express = require('express')
const path = require('path')
const formidable = require('formidable')
const mv = require('mv');
const app = express()
const port = 4455
const MOUNT_PATH = "/usr/share/pv"
app.get('/', (req, res) => res.sendFile(__dirname, "index.html")))
app.post('/upload', (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
let myFile = files.myFile
var oldpath = myFile.path;
var newpath = path.join(MOUNT_PATH, myFile.name);
mv(oldpath, newpath, (err) => {
if (err) return res.send(err);
res.send("Upload complete")
});
});
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
Note: mv is used instead of fs.rename because the latter cannot move files onto another device
Note 2: /usr/share/pv is used as mounting point. /usr/app, where the app is located, does not seem to work