Examples are available in examples folder:
- Simple Default Persistent Volume
- Pod Template with Persistent Volume
- AWS-based cluster with data replication and Persistent Volumes minimal and medium Zookeeper installations
k8s cluster administrator provision storage to applications (users) via PersistentVolume
objects.
Applications (users) claim storage with PersistentVolumeClaim
objects and then mount claimed PersistentVolume
s into filesystem via volumeMounts
+volumes
.
PersistentVolume
can be created as:
- Manual volume provisioning. Cluster administrator manually make calls to storage (cloud) provider to provision new storage volumes, and then create
PersistentVolume
objects to represent those volumes in Kubernetes. Users claim thosePersistentVolume
s later viaPersistentVolumeClaim
s - Dynamic volume provisioning. No need for cluster administrators to pre-provision storage manually.
Storage resources are dynamically provisioned by special software module, called provisioner, which is specified by the
StorageClass
object.StorageClass
es abstract the underlying storage provider with all parameters (such as disk type or location).StorageClass
es use software modules - provisioners that are specific to the storage platform or cloud provider to give Kubernetes access to the physical media being used.
Applications (users) refer StorageClass
by name in the PersistentVolumeClaim
with storageClassName
parameter.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: mytestns
spec:
storageClassName: my-storage-class
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
Storage class name - my-storage-class
in this example - is specific for each k8s installation and has to be provided (announced to applications(users)) by cluster administrator.
However, this is not convenient and sometimes we'd like to just use any available storage, without bothering to know what storage classes are available in this k8s installation.
The cluster administrator have an option to specify a default StorageClass
.
When present, the user can create a PersistentVolumeClaim
having no storageClassName
specified, simplifying the process and reducing required knowledge of the underlying storage provider.
Important notes on PersistentVolumeClaim
- if
storageClassName
is not specified, defaultStorageClass
(must be specified by cluster administrator) would be used for provisioning - if
storageClassName
is set to an empty string (""), noStorageClass
will be used, and thus, dynamic provisioning is efficiently disabled for thisPersistentVolumeClaim
. Available PVs that do not have anystorageClassName
specified will be considered for binding to this PVC - if
storageClassName
is set, then the matchingStorageClass
will be used for provisioning
We can use kubectl
to check for StorageClass
objects. Here we use cluster created with kops
kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER AGE
default kubernetes.io/aws-ebs 1d
gp2 (default) kubernetes.io/aws-ebs 1d
We can see two storage classes available:
- named as default
- named as gp2 which is the default
StorageClass
We can take a look inside them as:
kubectl get storageclasses.storage.k8s.io default -o yaml
kubectl get storageclasses.storage.k8s.io gp2 -o yaml
What we can see, that, actually, those StorageClass
es are equal:
metadata:
labels:
k8s-addon: storage-aws.addons.k8s.io
name: gp2
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Delete
volumeBindingMode: Immediate
metadata:
labels:
k8s-addon: storage-aws.addons.k8s.io
name: default
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Delete
volumeBindingMode: Immediate
What does this mean - we can specify our PersistentVolumeClaim
object with either:
- no
storageClassName
specified (just omit this field) - and in this caseStorageClass
named gp2 would be used (because it is the default one) or - specify
storageClassName: default
and in this case StorageClass
named default would be used. The result would be the same as when StorageClass
named gp2 used (which is actually the default StorageClass
in the system)
Pods use PersistentVolumeClaim
as volume.
PersistentVolumeClaim
must exist in the same namespace as the pod using the claim.
The k8s inspects the PersistentVolumeClaim
to find appropriate PersistentVolume
and mounts that PersistentVolume
into pod's filesystem via volumeMounts
.
A Pod refers "volumes: name" via "volumeMounts: name" in Pod or Pod Template as:
# ...
# excerpt from Pod or Pod Template manifest
# ...
containers:
- name: myclickhouse
image: clickhouse
volumeMounts:
- mountPath: "/var/lib/clickhouse"
name: my-volume
This "volume" definition can either be the final object description of different types, such as:
Volume of type emptyDir
# ...
# excerpt from manifest
# ...
volumes:
- name: my-volume
emptyDir: {}
Volume of type hostPath
# ...
# excerpt from StatefulSet manifest
# ...
volumes:
- name: my-volume
hostPath:
path: /local/path/
or can refer to PersistentVolumeClaim
as:
# ...
# excerpt from manifest
# ...
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: my-claim
where minimal PersistentVolumeClaim
can be specified as following:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Pay attention, that there is no storageClassName
specified - meaning this PersistentVolumeClaim
will claim PersistentVolume
of explicitly specified default StorageClass
.
More details on storageClassName
More details on PersistentVolumeClaim
Example on how this persistentVolumeClaim
named my-pvc
can be used in Pod spec:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
volumes:
- name: www
persistentVolumeClaim:
claimName: my-pvc
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
StatefulSet
shortcuts the way, jumping from volumeMounts
directly to volumeClaimTemplates
, skipping volume
.
More details in StatefulSet description
StatefulSet example:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
Pay attention to .spec.template.spec.containers.volumeMounts
:
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
refers directly to:
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
As we have discussed in AWS-specific section, AWS provides gp2 volumes as default media.
Let's create encrypted volume based on the same gp2 volume.
Specify special StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: encrypted-gp2
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
fsType: ext4
encrypted: "true"
reclaimPolicy: Delete
volumeBindingMode: Immediate
and use it with PersistentVolumeClaim
:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: encrypted-pvc
spec:
storageClassName: encrypted-gp2
accessModes:
- ReadWriteOnce
volumeMode: Block
resources:
requests:
storage: 1Gi