The Nautilus DevOps team is working on a Kubernetes template to deploy a web application on the cluster. There are some requirements to create/use persistent volumes to store the application code, and the template needs to be designed accordingly. Please find more details below:
Create a
PersistentVolumenamed aspv-devops. Configure thespecas storage class should bemanual, set capacity to3Gi, set access mode toReadWriteOnce, volume type should behostPathand set path to/mnt/itadmin(this directory is already created, you might not be able to access it directly, so you need not to worry about it).Create a
PersistentVolumeClaimnamed aspvc-devops. Configure thespecas storage class should bemanual, request3Giof the storage, set access mode toReadWriteOnce.Create a
podnamed aspod-devops, mount the persistent volume you created with claim namepvc-devopsat document root of the web server, the container within the pod should be named ascontainer-devopsusing imagehttpdwithlatesttag only (remember to mention the tag i.ehttpd:latest).Create a node port type service named
web-devopsusing node port30008to expose the web server running within the pod.
Understanding Persistent Volumes and Claims
What is a Persistent Volume?
A Persistent Volume (PV) is a piece of storage in the cluster that has been provisioned by an administrator. It is a cluster resource, similar to a node, and is independent of any individual pod.
What is a Persistent Volume Claim?
A Persistent Volume Claim (PVC) is a request for storage by a user. It is similar to a pod in that a pod consumes node resources, while a PVC consumes PV resources.
The Relationship Between PV and PVC
┌─────────────────────────────────────────────────────────────────────────────┐
│ Persistent Volume Workflow │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Administrator: Creates PersistentVolume (PV) │ │
│ │ - Defines storage capacity │ │
│ │ - Sets access modes │ │
│ │ - Specifies storage type (hostPath, NFS, etc.) │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ User: Creates PersistentVolumeClaim (PVC) │ │
│ │ - Requests storage capacity │ │
│ │ - Specifies access modes │ │
│ │ - Binds to a matching PV │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Pod: Uses PVC to mount storage │ │
│ │ - References PVC in volumes section │ │
│ │ - Mounts volume at specified path │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
What We Will Build
We are going to deploy a complete web application with persistent storage:
| Component | Specification |
|---|---|
| PersistentVolume |
pv-devops – 3Gi, manual storage class, hostPath |
| PersistentVolumeClaim |
pvc-devops – 3Gi request |
| Pod |
pod-devops – httpd web server |
| Service |
web-devops – NodePort on 30008 |
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ PersistentVolume: pv-devops │ │
│ │ - Capacity: 3Gi │ │
│ │ - Access Mode: ReadWriteOnce │ │
│ │ - Storage Class: manual │ │
│ │ - HostPath: /mnt/itadmin │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ PersistentVolumeClaim: pvc-devops │ │
│ │ - Requests: 3Gi │ │
│ │ - Access Mode: ReadWriteOnce │ │
│ │ - Storage Class: manual │ │
│ │ - Bound to: pv-devops │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Pod: pod-devops │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Container: container-devops │ │ │
│ │ │ Image: httpd:latest │ │ │
│ │ │ Port: 80 │ │ │
│ │ │ Volume: pvc-storage → pvc-devops │ │ │
│ │ │ Mount: /usr/local/apache2/htdocs │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Service: web-devops │ │
│ │ - Type: NodePort │ │
│ │ - Selector: app=httpd │ │
│ │ - Port: 80 │ │
│ │ - NodePort: 30008 │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ http://<node-ip>:30008 │
└─────────────────────────────────────────────────────────────────────────────┘
Step-by-Step Implementation
Step 1: Create the PersistentVolume
Create a file named pv-devops.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-devops
spec:
storageClassName: manual
capacity:
storage: 3Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/itadmin
Explanation:
-
storageClassName: manual– Identifies the storage class -
capacity.storage: 3Gi– Allocates 3 gigabytes of storage -
accessModes: ReadWriteOnce– Can be mounted as read-write by a single node -
hostPath.path: /mnt/itadmin– Uses the host node's filesystem
Step 2: Create the PersistentVolumeClaim
Create a file named pvc-devops.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-devops
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
Explanation:
- The
storageClassNamemust match the PV's storage class - The
accessModesmust match the PV's access modes -
resources.requests.storage– Requests 3Gi of storage
Step 3: Create the Pod
Create a file named pod-devops.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-devops
labels:
app: httpd
spec:
containers:
- name: container-devops
image: httpd:latest
ports:
- containerPort: 80
volumeMounts:
- name: pvc-storage
mountPath: /usr/local/apache2/htdocs
volumes:
- name: pvc-storage
persistentVolumeClaim:
claimName: pvc-devops
Explanation:
-
labels.app: httpd– Used by the service for selection -
volumeMounts.mountPath– The document root for httpd -
persistentVolumeClaim.claimName– References the PVC
Step 4: Create the Service
Create a file named service-devops.yaml:
apiVersion: v1
kind: Service
metadata:
name: web-devops
spec:
type: NodePort
selector:
app: httpd
ports:
- port: 80
targetPort: 80
nodePort: 30008
Explanation:
-
type: NodePort– Exposes the service on a static port -
selector.app: httpd– Routes traffic to pods with this label -
nodePort: 30008– The port on each node
Step 5: Apply the Configurations
kubectl apply -f pv-devops.yaml
kubectl apply -f pvc-devops.yaml
kubectl apply -f pod-devops.yaml
kubectl apply -f service-devops.yaml
Output:
persistentvolume/pv-devops created
persistentvolumeclaim/pvc-devops created
pod/pod-devops created
service/web-devops created
Verification
Step 1: Verify the PersistentVolume
kubectl get pv
Output:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS
pv-devops 3Gi RWO Retain Bound default/pvc-devops manual
Step 2: Verify the PersistentVolumeClaim
kubectl get pvc
Output:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
pvc-devops Bound pv-devops 3Gi RWO manual
Step 3: Verify the Pod
kubectl get pods
Output:
NAME READY STATUS RESTARTS AGE
pod-devops 1/1 Running 0 84s
Step 4: Verify the Service
kubectl get services
Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 32m
web-devops NodePort 10.43.193.63 <none> 80:30008/TCP 90s
Step 5: Verify the Endpoints
kubectl get endpoints web-devops
Output:
NAME ENDPOINTS AGE
web-devops 10.22.0.9:80 2m35s
Accessing the Application
The application can now be accessed at:
http://<node-ip>:30008
Default Httpd Page
When accessing the URL, you should see the default Apache HTTP Server welcome page, confirming that the web server is running and the persistent volume is mounted correctly.
Key Concepts
Access Modes
| Access Mode | Description |
|---|---|
ReadWriteOnce (RWO) |
Volume can be mounted as read-write by a single node |
ReadOnlyMany (ROX) |
Volume can be mounted as read-only by many nodes |
ReadWriteMany (RWX) |
Volume can be mounted as read-write by many nodes |
Storage Classes
Storage classes provide a way to define different types of storage:
-
manual– For manually provisioned storage -
standard– For dynamically provisioned storage - Custom classes – For specific storage providers (AWS EBS, GCE PD, etc.)
Reclaim Policies
| Policy | Description |
|---|---|
Retain |
Volume is retained after PVC deletion (manual cleanup required) |
Recycle |
Volume is scrubbed and made available again (deprecated) |
Delete |
Volume is deleted when PVC is deleted |
HostPath Volume
hostPath mounts a directory from the host node's filesystem. This is useful for:
- Development and testing
- Accessing host system files
- Single-node clusters
For production environments, consider using network-attached storage like NFS, or cloud provider storage solutions.
Troubleshooting
PVC Stuck in Pending State
# Check the PVC status
kubectl get pvc
# View detailed PVC information
kubectl describe pvc pvc-devops
# Check for available PVs
kubectl get pv
# Verify storage class and access modes match
Pod Fails to Start
# Check pod status
kubectl get pods
# View pod details
kubectl describe pod pod-devops
# Check pod logs
kubectl logs pod-devops
Service Not Accessible
# Check service status
kubectl get services
# Check endpoints
kubectl get endpoints web-devops
# Verify NodePort
kubectl get service web-devops -o yaml | grep nodePort
Best Practices
1. Use Appropriate Storage Classes
Choose the right storage class for your workload:
-
manual– For testing and development -
standard– For production with dynamic provisioning - Custom classes – For specific performance or availability requirements
2. Set Resource Requests
Always set appropriate storage requests:
- Request only what you need
- Monitor usage to adjust requests
3. Use Reclaim Policies Wisely
-
Retain– For important data that should not be accidentally deleted -
Delete– For temporary or disposable data
4. Consider PersistentVolume Snapshots
For production workloads, consider using volume snapshots for backup and disaster recovery.
5. Monitor Storage Usage
Use monitoring tools to track storage usage and capacity.
Summary
In this challenge, we successfully:
- Created a PersistentVolume with 3Gi capacity, hostPath storage, and manual storage class
- Created a PersistentVolumeClaim requesting 3Gi of storage
- Deployed a pod running the httpd web server with the PVC mounted at the document root
- Exposed the application using a NodePort service on port 30008
This deployment pattern is foundational for stateful applications in Kubernetes. Understanding how to manage persistent storage is essential for running production workloads like databases, content management systems, and any application that requires data persistence.
Useful Commands Reference
| Command | Purpose |
|---|---|
kubectl get pv |
List all PersistentVolumes |
kubectl get pvc |
List all PersistentVolumeClaims |
kubectl describe pv pv-devops |
Detailed PV information |
kubectl describe pvc pvc-devops |
Detailed PVC information |
kubectl get pods --show-labels |
List pods with labels |
kubectl get endpoints web-devops |
Check service endpoints |
kubectl describe service web-devops |
Detailed service information |
kubectl logs pod-devops |
View pod logs |
kubectl get nodes -o wide |
Get node IP addresses |
Top comments (0)