PV and PVC in K8S

Think of the relationship between a PersistentVolume (PV) and a PersistentVolumeClaim (PVC) like a Job Posting and a Resume. One is the resource available, and the other is the request for that resource.

In Kubernetes, this system allows developers to request storage without needing to know the messy details of the underlying infrastructure (like AWS EBS IDs or NFS IP addresses).


The Breakdown

1. PersistentVolume (PV)

A PV is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes. It is a cluster-level resource, meaning it exists independently of any individual Pod.

  • Analogy: A physical hard drive sitting on a shelf.
  • Scope: Cluster-wide (not tied to a namespace).
  • Details: Defines the capacity, access modes (ReadWriteOnce, ReadOnlyMany), and the actual storage backend (NFS, Cloud Disk, local path).

2. PersistentVolumeClaim (PVC)

A PVC is a request for storage by a user. It’s the “ticket” a developer uses to get a slice of storage for their application.

  • Analogy: A requisition form saying, “I need 5GB of space with fast read/write access.”
  • Scope: Namespace-specific (tied to where your Pod lives).
  • Details: Defines how much storage is needed and the required access mode.

How They Work Together

The interaction follows a specific lifecycle:

PhaseDescription
ProvisioningStorage is created. Static: Admin creates PVs manually. Dynamic: A StorageClass creates a PV automatically when a PVC is made.
BindingThe control plane looks for a PV that matches the PVC’s requirements. Once found, they are “bound” together in a 1-to-1 mapping.
UsingThe Pod uses the PVC as a volume. The cluster looks up the bound PV and mounts it into the container.
ReclaimingWhen a user is done, the Reclaim Policy (Retain, Recycle, or Delete) determines what happens to the data on the PV.

Key Differences at a Glance

FeaturePersistentVolume (PV)PersistentVolumeClaim (PVC)
RoleThe actual storage “provider.”The storage “consumer.”
ResponsibilityUsually managed by Cluster Admins.Usually created by Developers.
LifecycleExists until manually deleted.Exists until the application is deleted.
LevelCluster-scoped.Namespace-scoped.

The YAML Link

In your Pod definition, you don’t point to the PV. You point to the PVC.

YAML

volumes:
- name: my-data
persistentVolumeClaim:
claimName: my-pvc-request # The Pod asks for the claim, not the disk!

Pro Tip: If you delete a PVC, the fate of the PV depends on the reclaimPolicy. If it’s set to Retain, the PV stays around but is marked “Released,” meaning no other PVC can use it until an admin manually scrubs it.

# Please edit the object below. Lines beginning with a ‘#’ will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: PersistentVolume
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{“apiVersion”:”v1″,”kind”:”PersistentVolume”,”metadata”:{“annotations”:{},”labels”:{“com.rbccm.appcode”:”ovi”},”name”:”ovi-dev2-phoenix”,”namespace”:””},”spec”:{“accessModes”:[“ReadWriteMany”],”capacity”:{“storage”:”50Gi”},”mountOptions”:[“nfsvers=3″],”nfs”:{“path”:”/ovi/kube/dev2″,”server”:”nfs.server.com”},”persistentVolumeReclaimPolicy”:”Retain”,”storageClassName”:”ovi”}}
pv.kubernetes.io/bound-by-controller: “yes”
creationTimestamp: 2019-05-02T21:34:10Z
finalizers:
– kubernetes.io/pv-protection
labels:
com.ovi.appcode: ovi
name: ovi-dev2-phoenix
resourceVersion: “203315676”
selfLink: /api/v1/persistentvolumes/ovi-dev2-phoenix
uid: 05de00f4-6d22-11e9-a8b7-0242ac11yyy
spec:
accessModes:
– ReadWriteMany
capacity:
storage: 50Gi
claimRef:     ———– >>>> if PVC is deleted —remove this part in spec ( PV is in released Status )
apiVersion: v1
kind: PersistentVolumeClaim
name: ovi-dev2-phoenix
namespace: te20-dev2
resourceVersion: “203305105”
uid: 7a0cbf09-fa8b-11e9-a244-0242ac11yyy
mountOptions:
– nfsvers=3
nfs:
path: /ovi/kube/dev2
server: nfs.server.com
persistentVolumeReclaimPolicy: Retain
storageClassName: ovi
status:
phase: Bound

 

in GCP

Static provisioning

gcloud beta compute disks create –size \

–size 1GB

–region  us-east1

ovi-disk

Understanding Kubernetes LimitRange for Resource Management

While a ResourceQuota sets a budget for the entire namespace (e.g., “This team gets 20 CPUs total”), a LimitRange sets the rules for the individual items inside that namespace.

It ensures that no single container is too big, too small, or creates chaos by forgetting to define its resource needs.


What a LimitRange Actually Does

A LimitRange provides four main “guardrails” for your Pods and Containers:

  1. Default Values: If a developer forgets to set requests or limits, the LimitRange automatically injects them.
  2. Minimum Constraints: Rejects any Pod that tries to request too little (preventing performance issues).
  3. Maximum Constraints: Rejects any Pod that tries to request too much (preventing one Pod from hogging a whole Node).
  4. Ratio Enforcement: Can limit the difference between a request and a limit (preventing extreme overcommitment).

Anatomy of a LimitRange

Here is what a typical configuration looks like:

YAML

apiVersion: v1
kind: LimitRange
metadata:
name: cpu-mem-limits
namespace: dev-team
spec:
limits:
- type: Container
# 1. Defaults (Injected if missing in Pod spec)
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
# 2. Boundaries (Pod will be rejected if outside these)
max:
cpu: "2"
memory: "1Gi"
min:
cpu: "100m"
memory: "128Mi"

LimitRange vs. ResourceQuota

It’s easy to confuse these two, but they solve different problems:

FeatureLimitRangeResourceQuota
FocusIndividual Pods/Containers.Total Namespace aggregate.
AnalogyA rule that no one at the buffet can take more than 2 slices of pizza at a time.A rule that the entire party can only eat 10 pizzas total.
ActionInjects defaults and validates sizes.Blocks creation if the “budget” is hit.
ScopeOne Pod’s health/impact.Overall cluster capacity management.

Important Gotchas

  • Existing Pods: If you apply a LimitRange today, it does not affect Pods already running. It only checks new Pods at the moment of creation (the “Admission” phase).
  • Storage Too: You can also use LimitRange to set min and max sizes for PersistentVolumeClaims (PVCs) to stop users from accidentally requesting 10TB of cloud storage.
  • Consistency: If you set a default limit but no default request, Kubernetes will often set the request to equal the limit.

Are you seeing specific “Forbidden” errors when trying to deploy a Pod, or are you looking to set up these guardrails for a new team?

A limit range, defined by a LimitRange object, provides constraints that can:

  • Enforce minimum and maximum compute resources usage per Pod or Container in a namespace.
  • Enforce minimum and maximum storage request per PersistentVolumeClaim in a namespace.
  • Enforce a ratio between request and limit for a resource in a namespace.
  • Set default request/limit for compute resources in a namespace and automatically inject them to Containers at runtime.

 

bash-4.2$ kubectl describe limitranges -n ovi
Name: ovi-limitrange
Namespace: ovi
Type             Resource     Min      Max    Default Request        Default Limit       Max Limit/Request Ratio
—- ——– — — ————— ————- ————————————————————
Container     memory     64Mi     32Gi     64Mi                            512Mi                10
Container        cpu         100m     16        100m                           500m                   8

 

Edit limit range

bash-4.2$ kubectl edit limitranges -n ovi

 

 

# Please edit the object below. Lines beginning with a ‘#’ will be ignored,

2 # and an empty file will abort the edit. If an error occurs while saving this file will be

3 # reopened with the relevant failures.

4 #

5 apiVersion: v1

6 kind: LimitRange

7 metadata:

8   annotations:

9     kubectl.kubernetes.io/last-applied-configuration: |

10       {“apiVersion”:”v1″,”kind”:”LimitRange”,”metadata”:{“annotations”:{},”name”:”ovi-limitrange”,”namespace”:”ovi”},”spec”:{“limits”:[{“default”:{“cpu”:”500m”,”m        emory”:”512Mi”},”defaultRequest”:{“cpu”:”100m”,”memory”:”64Mi”},”max”:{“cpu”:”16″,”memory”:”32Gi”},”maxLimitRequestRatio”:{“cpu”:”8″,”memory”:”10″},”min”:{“cpu”:”10        0m”,”memory”:”64Mi”},”type”:”Container”}]}}

11   creationTimestamp: 2019-02-13T15:54:44Z

12   name: ovi-limitrange

13   namespace: ovi

14   resourceVersion: “127366586”

15   selfLink: /api/v1/namespaces/te20/limitranges/ovi-limitrange

16   uid: ae91e929-2fa7-11e9-9dd5-0242ac110005

17 spec:

18   limits:

19   – default:

20       cpu: 500m

21       memory: 512Mi

22     defaultRequest:

23       cpu: 100m

24       memory: 64Mi

25     max:

26       cpu: “16”

27       memory: 32Gi

28     maxLimitRequestRatio:

29       cpu: “8”

30       memory: “10”

31     min:

32       cpu: 100m

33       memory: 64Mi

34     type: Container

 

Continue reading →

k8S

kubectl get deployment -n kube-sysdig

kubectl scale deployment sysdigcloud-api –replicas=0 -n kube-sysdig

kubectl scale deployment sysdigcloud-worker –replicas=0 -n kube-sysdig

kubectl scale deployment sysdigcloud-collector –replicas=0 -n kube-sysdig

kubectl get deployment -n kube-sysdig

kubectl scale deployment sysdigcloud-worker –replicas=1 -n kube-sysdig

kubectl scale deployment sysdigcloud-collector –replicas=1 -n kube-sysdig

kubectl scale deployment sysdigcloud-api –replicas=1 -n kube-sysdig

kubectl get deployment -n kube-sysdig

 

bash-4.2$ kubectl logs sysdigcloud-collector-bc45fc -n kube-sysdig

 

*** Resource quota & limitrange

kubectl get limitrange –namespace=ovi –output=yaml

 

C:\ovi\docker

λ kubectl describe limitrange  –namespace=ovi   

Name:       ovi-limitrange

Namespace:  wdu0

Type          Resource      Min    Max  Default Request  Default Limit  Max Limit/Request Ratio

—-          ——–  —   —  —————  ————-  ———————–

Container     cpu          100m    4    100m             500m           –

Container    memory    64Mi   4Gi  64Mi             512Mi

 

C:\ovi\docker

λ kubectl describe quota  ovi-resourcequota -n ovi   

Name:            ovi-resourcequota

Namespace:     ovi

Resource               Used    Hard

——–                    —-    —-

limits.cpu               5500m   8

limits.memory      5632Mi  12Gi

pods                        4       10

requests.cpu           2300m   8

requests.memory    2624Mi  12Gi

 

C:\ovi\docker

λ kubectl get limitrange –namespace=ovi –output=yaml

kubectl edit quota ovi-resourcequota -n ovi

kubectl edit limitrange –namespace=wdu0 –output=yaml

 

λ kubectl get apiservices
NAME AGE
v1. 1y
v1.apps 309d
v1.authentication.k8s.io 1y
v1.authorization.k8s.io 1y
v1.autoscaling 1y
v1.batch 1y
v1.networking.k8s.io 1y
v1.rbac.authorization.k8s.io 309d
v1.storage.k8s.io 1y
v1alpha1.admissionregistration.k8s.io 1y
v1beta1.admissionregistration.k8s.io 309d
v1beta1.apiextensions.k8s.io 1y
v1beta1.apps 1y
v1beta1.authentication.k8s.io 1y
v1beta1.authorization.k8s.io 1y
v1beta1.batch 1y
v1beta1.certificates.k8s.io 1y
v1beta1.compose.docker.com 58d
v1beta1.events.k8s.io 309d
v1beta1.extensions 1y
v1beta1.metrics.k8s.io 228d
v1beta1.policy 1y
v1beta1.rbac.authorization.k8s.io 309d
v1beta1.scheduling.k8s.io 309d
v1beta1.storage.k8s.io 1y
v1beta2.apps 1y
v1beta2.compose.docker.com 58d
v2beta1.autoscaling 1y