{"id":590,"date":"2021-06-17T07:14:00","date_gmt":"2021-06-17T07:14:00","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=590"},"modified":"2021-06-17T07:14:00","modified_gmt":"2021-06-17T07:14:00","slug":"persistent-volume-google-kubernetes-engine","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=590","title":{"rendered":"How to Add Persistent Volume in Google Kubernetes Engine"},"content":{"rendered":"<p>In this blog, you will learn how to setup Persistent Volume For the <a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-google-cloud\/\" rel=\"noreferrer noopener\">GKE Kubernetes cluster.<\/a><\/p>\n<p>If you want to preserve the data even after a pod deletion or pod failures, you should use persistent volumes.<\/p>\n<h2 id=\"about-gke-persistent-volumes\">About GKE Persistent Volumes<\/h2>\n<p>In GKE, you can provision a Google Cloud Persistent disk (Compute Engine Disks) to be used as a persistent volume in the <a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer noopener\">Kubernetes cluster<\/a>.<\/p>\n<p>You can dynamically provision the persistent volumes on demand using the Kubernbetes persistent volume claim manifests.<\/p>\n<p>Based on the type of persistent disk storage class, GKE will ensure the volume is provisioned and makes it available for use in the cluster.<\/p>\n<p>Following are some important concepts you need to be aware of when using persistent volumes with GKE.<\/p>\n<ol>\n<li>You can use both google persistent disk and Google Filestore (NFS) as persistent volumes for GKE.<\/li>\n<li>Persistent disks can be regional (offers high durability) or zonal based on the requirements.<\/li>\n<li>You can also use a pre-existing persistent disk with data as a GKE persistent volume.<\/li>\n<li>By default, dynamic provisioning uses the <code>pd-standard<\/code> disk type as a storage class. It is one of the <a href=\"https:\/\/cloud.google.com\/compute\/docs\/disks?ref=devopscube.com\" rel=\"noreferrer noopener\">four standard persistent disk types<\/a> offered by Google Cloud.<\/li>\n<li>You can back up the persistent volumes using a Volume snapshot. This feature works only if you have the Compute Engine persistent disk CSI Driver enabled in the cluster.<\/li>\n<\/ol>\n<h2 id=\"setup-persistent-volume-for-gke\">Setup Persistent Volume For GKE<\/h2>\n<p>We will do the following.<\/p>\n<ol>\n<li>Create a storage class<\/li>\n<li>Provision a Persistent volume using the storage class.<\/li>\n<li>Test a deployment with the persistent volume.<\/li>\n<\/ol>\n<p>Lets gets started with the setup.<\/p>\n<h2 id=\"create-a-storage-class-for-gke\">Create a storage class for GKE<\/h2>\n<p>Storage class is a simple way of segregating the storage options.<\/p>\n<p>To put simply, a storage class defines what type of storage to be provisioned.<\/p>\n<p>For example, we can classify our storage class as <code>gold<\/code> and <code>silver<\/code>. These names are arbitrary and use a name that is meaningful to you.<\/p>\n<p>Gold storage class uses the <code>pd-ssd<\/code> persistent disk type for high IOPS applications (to be used with databases). While silver storage class uses the <code>pd-standard<\/code> volume type to be used for backups and normal disk operations.<\/p>\n<p>These storage class segregations are completely based on the project requirements.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\">Note: There are default storage classes available in GKE which are backed by <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">pd-standard<\/code> disks. If you don&#8217;t specify a storage class while provisioning a PV, the default storage class is considered.<\/div>\n<\/div>\n<p>Lets create a gold storage class.<\/p>\n<p>Save the following manifest as <code>storage-class.yaml<\/code>.<\/p>\n<pre><code>apiVersion: storage.k8s.io\/v1\nkind: StorageClass\nmetadata:\n  name: gold\nprovisioner: kubernetes.io\/gce-pd\nvolumeBindingMode: Immediate\nallowVolumeExpansion: true\nreclaimPolicy: Delete\nparameters:\n  type: pd-standard\n  fstype: ext4\n  replication-type: none<\/code><\/pre>\n<p>Create the storage class.<\/p>\n<pre><code>kubectl apply -f storage-class.yaml<\/code><\/pre>\n<p>A little explanation about the parameters.<\/p>\n<ol>\n<li><strong>Type: <\/strong> supports <code>pd-standard<\/code> &amp; <code>pd-ssd<\/code>. If you don&#8217;t specify anything, it defaults <code>pd-standard<\/code><\/li>\n<li><strong>fstype:<\/strong> supports <code>ext4<\/code> and <code>xfs<\/code>. Defaults to <code>ext4<\/code>.<\/li>\n<li><strong>replication-type: <\/strong>This decides whether the disk is zonal or regional. If you don&#8217;t specify <code>regional-pd<\/code>, it defaults to a zonal disk.<\/li>\n<li><strong>allowVolumeExpansion:<\/strong> With this parameter, you can expand the persistent volume if required.<\/li>\n<li><strong>volumeBindingMode:<\/strong> There are two modes. <code>Immediate<\/code> and <code>WaitForFirstConsumer<\/code>. In cases where the storage is not accessible from all the nodes, use <code>WaitForFirstConsumer<\/code> so that volume binding will happen after the pod gets created.<\/li>\n<\/ol>\n<h2 id=\"create-a-persistent-volume-using-pvc-on-gke\">Create a Persistent Volume using PVC on GKE<\/h2>\n<p>To create a persistent volume you need to create a Persistent Volume claim.<\/p>\n<p><code>persistentVolumeClaim<\/code> is the way to request storage based on a storage class and use it with a pod. The pod to Persistent volume mapping happens through PVC.<\/p>\n<p>Meaning, when you request for a PVC, a persistent volume (google persistent disk) will be dynamically provisioned based on the <code>pd-standard<\/code> or <code>pd-ssd<\/code> the parameter you specified in the storage class.<\/p>\n<p>So PVC (Request) &#8211;&gt; Storage Class (Defines Type of disk) &#8211;&gt; Persistent Volume (Google Persistent Disk)<\/p>\n<p>The&nbsp;<strong>accessModes<\/strong>&nbsp;in&nbsp;<strong>persistentVolume<\/strong>&nbsp;defines how a volume should be mounted. There are 4 access modes available:<\/p>\n<ol>\n<li><code>ReadWriteOnce<\/code>&nbsp;(RWO): This access mode allows the volume to be mounted as read-write by a single worker node at a time. All the pods on that specific node can access the volume.<\/li>\n<li><code>ReadWriteMany<\/code>&nbsp;(RWX): It defines that the volume can be concurrently mounted to multiple worker nodes with read-write access for any Pod.<\/li>\n<li><code>ReadOnlyMany<\/code>&nbsp;(ROX): This access mode allows multiple worker nodes to mount the volume simultaneously, but only in read-only mode.<\/li>\n<li><code>ReadWriteOncePod<\/code>&nbsp;(RWOP): Only a single pod in the entire cluster can gain access to the volume.<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note:<\/strong><\/b> ReadWriteMany option is not supported in persistent volumes backed by Google persistent disk. If you have a use case to use ReadWriteMany volumes, you can consider google filestore backed persistent volumes for GKE.<\/div>\n<\/div>\n<p>In our example, we are creating a <code>PVC<\/code> using gold storage class with <code>50GB<\/code> storage in the <code>default<\/code> namespace. You can also assign a custom namespace under<code> metadata<\/code><\/p>\n<p>Save the manifest as <code>pvc.yaml<\/code><\/p>\n<pre><code>apiVersion: v1\n kind: PersistentVolumeClaim\n metadata:\n   name: webapps-storage\n spec:\n   storageClassName: gold\n   accessModes:\n     - ReadWriteOnce\n   resources:\n     requests:\n       storage: 50Gi<\/code><\/pre>\n<p>Now, create the PVC.<\/p>\n<pre><code>kubeactl apply -f pvc.yaml<\/code><\/pre>\n<p>You can check the pv and pvc using the following commands.<\/p>\n<pre><code>kubectl get pv\nkubectl get pvc<\/code><\/pre>\n<p>If you check the compute engine disks, you will see a 50GB disk created as shown below.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-12-39.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"586\" height=\"347\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<p>Since the gold storage class <code>volumeBindingMode<\/code> is immediate, you will see the volume provisioned and the claim available for pods to use.<\/p>\n<p>If your storage class binding mode is <code>WaitForFirstConsumer<\/code> , after deploying the PVC, you will see PVC status in a pending state. Because only after a pod is created with a PVC request, kubernetes creates the persistent volume<\/p>\n<p>The following image shows the difference between <code>immediate<\/code> and <code>WaitForFirstConsumer<\/code> modes.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-11-44.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"562\" height=\"337\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h2 id=\"creating-persistent-volumes-from-existing-google-compute-disks\">Creating Persistent Volumes From Existing Google Compute Disks<\/h2>\n<p>You can create persist volumes volumes from existing google compute disks.<\/p>\n<p>For demonstration purpose, I am creating a compute disk named <code>gke-pv<\/code> of size 50GB<\/p>\n<pre><code>gcloud compute disks create gke-pv  --zone=us-central1-a --size=50GB<\/code><\/pre>\n<p>Now we have a disk available to be used as PV in GKE.<\/p>\n<p>Next step is to,<\/p>\n<ol>\n<li>Create a Persistent volume named <code>app-storage<\/code> from the <code>gke-pv<\/code> disk<\/li>\n<li>To use the persistent volume with the pod, we will create a persistent volume claim with the same name we use in the PV <code>claimRef<\/code>, ie <code>app-storage-claim<\/code><\/li>\n<\/ol>\n<p>Save the following manifest as <code>disk-pv.yaml<\/code><\/p>\n<pre><code>apiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: app-storage\nspec:\n  storageClassName: \"apps\"\n  capacity:\n    storage: 50\n  accessModes:\n    - ReadWriteOnce\n  claimRef:\n    namespace: default\n    name: app-storage-claim\n  gcePersistentDisk:\n    pdName: gke-pv\n    fsType: ext4\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: app-storage-claim\nspec:\n  storageClassName: \"apps\"\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 50<\/code><\/pre>\n<p>Create the PV &amp; PVC.<\/p>\n<pre><code>kubectl apply -f disk-pv.yaml<\/code><\/pre>\n<p>You can check the pv and pvc using the following commands.<\/p>\n<pre><code>kubectl get pv\nkubectl get pvc<\/code><\/pre>\n<h2 id=\"example-gke-pod-with-persistent-volume\">Example GKE Pod With Persistent Volume<\/h2>\n<p>Now that we know the two ways to use google persistent disk as GKE persistent volumes, we will look at using the persistent volume in a pod.<\/p>\n<p>To mount a persistent volume to the pod, we use the <strong>Persistent volume claim<\/strong> name in the <code>volumes<\/code> section, and we use the volume name in the <code>volumeMounts<\/code> section with the container path to mount.<\/p>\n<p>In out example, we will be mounting the  <code>\/usr\/share\/nginx\/html<\/code> path to the persistent volume.<\/p>\n<pre><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx-app-pod\nspec:\n  volumes:\n    - name: app-storage\n      persistentVolumeClaim:\n        claimName: app-storage-claim\n  containers:\n    - name: nginx-app-container\n      image: nginx\n      ports:\n        - containerPort: 80\n          name: \"http-server\"\n      volumeMounts:\n        - mountPath: \"\/usr\/share\/nginx\/html\"\n          name: app-storage<\/code><\/pre>\n<h2 id=\"example-gke-deployment-with-persistent-volume-claim\">Example GKE Deployment With Persistent Volume Claim<\/h2>\n<p>Lets try using the persistent volume on a Jenkins delployment.<\/p>\n<p>This <a href=\"https:\/\/devopscube.com\/setup-jenkins-on-kubernetes-cluster\/\" rel=\"noreferrer noopener\">Jenkins deployment<\/a> creates a pod with all its data mounted to the persistent volume. So, even if you delete the pod, a new pod will come up and mount itself to the persistent volume keeping the same old state of Jenkins.<\/p>\n<p>Here is a little explanation about the deployment<\/p>\n<ol>\n<li>Create a Persistent volume claim with the <code>gold<\/code> storage class.<\/li>\n<li>In deployment, Create a  <code>Volumes<\/code> definition named <code>jenkins-data<\/code> and add the <code>jenkins-pv-claim<\/code> to be added as a volume to the container<\/li>\n<li>In container <code>spec<\/code>, under <code>volumeMounts<\/code>, we define the volume name and mount path <code>\/var\/jenkins_home<\/code> for the container.<\/li>\n<li>A service will expose Jenkins on NodePort <code>32000<\/code><\/li>\n<\/ol>\n<p>Save the following manifest as <code>jenkins.yaml<\/code>. It has the PVC, deployment and service definitions.<\/p>\n<pre><code># Persistent Volume Claim\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: jenkins-pv-claim\nspec:\n  storageClassName: gold\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 50Gi\n\n# Deployment Config\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: jenkins-deployment\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: jenkins\n  template:\n    metadata:\n      labels:\n        app: jenkins\n    spec:\n      securityContext:\n            fsGroup: 1000 \n            runAsUser: 1000\n      containers:\n        - name: jenkins\n          image: jenkins\/jenkins:lts\n          resources:\n            limits:\n              memory: \"2Gi\"\n              cpu: \"1000m\"\n            requests:\n              memory: \"500Mi\"\n              cpu: \"500m\"\n          ports:\n            - name: httpport\n              containerPort: 8080\n            - name: jnlpport\n              containerPort: 50000\n          livenessProbe:\n            httpGet:\n              path: \"\/login\"\n              port: 8080\n            initialDelaySeconds: 90\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 5\n          readinessProbe:\n            httpGet:\n              path: \"\/login\"\n              port: 8080\n            initialDelaySeconds: 60\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          volumeMounts:\n            - name: jenkins-data\n              mountPath: \/var\/jenkins_home         \n      volumes:\n        - name: jenkins-data\n          persistentVolumeClaim:\n              claimName: jenkins-pv-claim\n\n# Service Config\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: jenkins-service\n  annotations:\n      prometheus.io\/scrape: 'true'\n      prometheus.io\/path:   \/\n      prometheus.io\/port:   '8080'\nspec:\n  selector: \n    app: jenkins\n  type: NodePort  \n  ports:\n    - port: 8080\n      targetPort: 8080\n      nodePort: 32000<\/code><\/pre>\n<p>Create the deployment.<\/p>\n<pre><code>kubectl apply -f jenkins.yaml<\/code><\/pre>\n<p>Once the deployment is up and running, you will be able to access the Jenkins server on any of the Node port <code>32000<\/code><\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>We have see how to setup persistent volume on a GKE cluster with few examples using pods and deployments.<\/p>\n<p>Hope this article helps.<\/p>\n<p>Let me know in the comment section, if you face any issues,<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/persistent-volume-google-kubernetes-engine\/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Add Persistent Volume in Google Kubernetes Engine \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/persistent-volume-google-kubernetes-engine\/<\/p>\n","protected":false},"author":1,"featured_media":591,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-590","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/590","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=590"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/590\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/591"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=590"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=590"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=590"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}