Wednesday, 22 October 2025

Deploy Drupal with MySQL on Kubernetes: Complete Setup

Our course you can check :-   Udemy course  


Ques:-  

We need to deploy a Drupal application on Kubernetes cluster. Application development team want to setup a fresh Drupal as they will do the installation on their own. Below you can find the requirements, they have shared with us.

1) Configure a persistent volume drupal-mysql-pv with hostPath = /drupal-mysql-data (/drupal-mysql-data directory already exists on the worker Node i.e jump host), 5Gi of storage and ReadWriteOnce access mode.

2) Configure one PersistentVolumeClaim named drupal-mysql-pvc with storage request of 3Gi and ReadWriteOnce access mode.

3) Create a deployment drupal-mysql with 1 replica, use mysql:5.7 image. Mount the claimed PVC at /var/lib/mysql.

4) Create a deployment drupal with 1 replica and use drupal:8.6 image.

4) Create a NodePort type service which should be named as drupal-service and nodePort should be 30095.

5) Create a service drupal-mysql-service to expose mysql deployment on port 3306.

6) Set rest of the configration for deployments, services, secrets etc as per your preferences. At the end you should be able to access the Drupal installation page .


Ans:-

Here is your single Kubernetes YAML manifest that includes all required resources to deploy a fresh Drupal application with a MySQL backend:

This manifest includes:

  • PersistentVolume and PersistentVolumeClaim for MySQL data
  • drupal-mysql Deployment with MySQL 5.7
  • drupal Deployment with Drupal 8.6
  • drupal-service as a NodePort service (port 30095)
  • drupal-mysql-service to expose MySQL internally
raj@jumphost ~$ cat pod.yaml

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: drupal-mysql-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /drupal-mysql-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: drupal-mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: drupal-mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drupal-mysql
  template:
    metadata:
      labels:
        app: drupal-mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: drupalroot
            - name: MYSQL_DATABASE
              value: drupaldb
            - name: MYSQL_USER
              value: drupaluser
            - name: MYSQL_PASSWORD
              value: drupalpass
          volumeMounts:
            - name: mysql-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-storage
          persistentVolumeClaim:
            claimName: drupal-mysql-pvc
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: drupal
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drupal
  template:
    metadata:
      labels:
        app: drupal
    spec:
      containers:
        - name: drupal
          image: drupal:8.6
          ports:
            - containerPort: 80
          env:
            - name: DRUPAL_DB_HOST
              value: drupal-mysql-service
            - name: DRUPAL_DB_NAME
              value: drupaldb
            - name: DRUPAL_DB_USER
              value: drupaluser
            - name: DRUPAL_DB_PASSWORD
              value: drupalpass
---
apiVersion: v1
kind: Service
metadata:
  name: drupal-service
spec:
  type: NodePort
  selector:
    app: drupal
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30095
---
apiVersion: v1
kind: Service
metadata:
  name: drupal-mysql-service
spec:
  selector:
    app: drupal-mysql
  ports:
    - port: 3306
      targetPort: 3306

raj@jumphost ~$ kubectl apply -f pod.yaml
persistentvolume/drupal-mysql-pv created
persistentvolumeclaim/drupal-mysql-pvc created
deployment.apps/drupal-mysql created
deployment.apps/drupal created
service/drupal-service created
service/drupal-mysql-service created

raj@jumphost ~$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                      STORAGECLASS   REASON   AGE
persistentvolume/drupal-mysql-pv                            5Gi        RWO            Retain           Available                                                      23s
persistentvolume/pvc-08ba2d61-5cd4-4204-8471-67fca87d377a   3Gi        RWO            Delete           Bound       default/drupal-mysql-pvc   standard                18s

NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/drupal-mysql-pvc   Bound    pvc-08ba2d61-5cd4-4204-8471-67fca87d377a   3Gi        RWO            standard       23s

raj@jumphost ~$ kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/drupal-f5499f965-xbghv          1/1     Running   0          69s
pod/drupal-mysql-84dccbc887-b4d5s   1/1     Running   0          69s

NAME                           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/drupal-mysql-service   ClusterIP   10.96.29.189   <none>        3306/TCP       69s
service/drupal-service         NodePort    10.96.171.61   <none>        80:30095/TCP   69s
service/kubernetes             ClusterIP   10.96.0.1      <none>        443/TCP        16m

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/drupal         1/1     1            1           69s
deployment.apps/drupal-mysql   1/1     1            1           69s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/drupal-f5499f965          1         1         1       69s
replicaset.apps/drupal-mysql-84dccbc887   1         1         1       69s
raj@jumphost ~$ 


Conclusion:-

In this hands-on tutorial, you'll learn how to deploy a fresh Drupal application backed by MySQL on a Kubernetes cluster. We'll walk through setting up persistent storage, configuring deployments, and exposing services using NodePort. By the end of this course, you'll be able to access the Drupal installation page and understand how to manage stateful applications in Kubernetes.

What you'll learn:

  • Create and bind PersistentVolumes and PersistentVolumeClaims
  • Deploy MySQL and Drupal containers using Kubernetes Deployments
  • Configure environment variables for Drupal-MySQL integration
  • Expose services using NodePort and ClusterIP
  • Access and verify the Drupal installation page
  • Understand Kubernetes resource relationships for stateful apps

Monday, 20 October 2025

Deploy PHP Applications on Kubernetes with Nginx and PHP-FPM

Our course you can check :-   Udemy course  


Ques:- 

Application Development team is planning to deploy one of the php-based applications on Kubernetes cluster. As per the recent discussion with DevOps team, they have decided to use nginx and phpfpm. Additionally, they also shared some custom configuration requirements. Below you can find more details. Please complete this task as per requirements mentioned below:

1) Create a service to expose this app, the service type must be NodePort, nodePort should be 30012.

2.) Create a config map named nginx-config for nginx.conf as we want to add some custom settings in nginx.conf.

a) Change the default port 80 to 8096 in nginx.conf.

b) Change the default document root /usr/share/nginx to /var/www/html in nginx.conf.

c) Update the directory index to index  index.html index.htm index.php in nginx.conf.

3.) Create a pod named nginx-phpfpm .

b) Create a shared volume named shared-files that will be used by both containers (nginx and phpfpm) also it should be a emptyDir volume.

c) Map the ConfigMap we declared above as a volume for nginx container. Name the volume as nginx-config-volume, mount path should be /etc/nginx/nginx.conf and subPath should be nginx.conf

d) Nginx container should be named as nginx-container and it should use nginx:latest image. PhpFPM container should be named as php-fpm-container and it should use php:8.2-fpm-alpine image.

e) The shared volume shared-files should be mounted at /var/www/html location in both containers. Copy /opt/index.php from jump host to the nginx document root inside the nginx container.

You can use any labels as per your choice


Ans:-

Here’s a complete Kubernetes setup based on your requirements for deploying a PHP-based application using nginx and php-fpm:

raj@jumphost ~$ cat pod.yaml


---

#Create the ConfigMap (nginx-config)

apiVersion: v1

kind: ConfigMap

metadata:

  name: nginx-config

data:

  nginx.conf: |

    events {}

    http {

      server {

        listen 8096;

        root /var/www/html;

        index index.html index.htm index.php;


        location / {

          try_files $uri $uri/ =404;

        }


        location ~ \.php$ {

          include fastcgi_params;

          fastcgi_pass 127.0.0.1:9000;

          fastcgi_index index.php;

          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        }

      }

    }


---

#Create the Pod (nginx-phpfpm)

apiVersion: v1

kind: Pod

metadata:

  name: nginx-phpfpm

  labels:

    app: php-nginx-app

spec:

  volumes:

    - name: shared-files

      emptyDir: {}

    - name: nginx-config-volume

      configMap:

        name: nginx-config

        items:

          - key: nginx.conf

            path: nginx.conf

  containers:

    - name: nginx-container

      image: nginx:latest

      volumeMounts:

        - name: shared-files

          mountPath: /var/www/html

        - name: nginx-config-volume

          mountPath: /etc/nginx/nginx.conf

          subPath: nginx.conf

    - name: php-fpm-container

      image: php:8.2-fpm-alpine

      volumeMounts:

        - name: shared-files

          mountPath: /var/www/html


---

#Create the Service (NodePort)

apiVersion: v1

kind: Service

metadata:

  name: php-nginx-service

spec:

  type: NodePort

  selector:

    app: php-nginx-app

  ports:

    - port: 8096

      targetPort: 8096

      nodePort: 30012



raj@jumphost ~$ kubectl apply -f pod.yaml

configmap/nginx-config created

pod/nginx-phpfpm created

service/php-nginx-service created


raj@jumphost ~$ kubectl get all

NAME               READY   STATUS    RESTARTS   AGE

pod/nginx-phpfpm   2/2     Running   0          16s


NAME                        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE

service/kubernetes          ClusterIP   10.96.0.1      <none>        443/TCP          32m

service/php-nginx-service   NodePort    10.96.42.191   <none>        8096:30012/TCP   16s


raj@jumphost ~$ kubectl get cm

NAME               DATA   AGE

kube-root-ca.crt   1      32m

nginx-config       1      22s


Copy index.php to the shared volume
After the pod is running, execute the following command from the jump host:

raj@jumphost ~$ kubectl cp /opt/index.php nginx-phpfpm:/var/www/html/index.php -c nginx-container

raj@jumphost ~$ 


Accessing the App

Once the file is copied, you can access the app via the Node IP and NodePort (30012)


Conclusion:-

Learn how to deploy a PHP-based web application on a Kubernetes cluster using Nginx and PHP-FPM. This hands-on tutorial walks you through creating a ConfigMap for custom Nginx configuration, setting up a multi-container Pod with shared volumes, and exposing your application using a NodePort service. Perfect for DevOps engineers, application developers, and Kubernetes enthusiasts looking to understand real-world deployment patterns.

What you'll learn:

  • Create and use ConfigMaps for custom Nginx configuration
  • Deploy multi-container Pods with shared volumes
  • Use emptyDir volumes for inter-container communication
  • Expose applications using NodePort services
  • Copy files into running containers using kubectl cp
  • Access deployed PHP apps via browser

Sunday, 19 October 2025

Deploy a Secure LEMP Stack on Kubernetes with Secrets, ConfigMaps & Environment Variables

Our course you can check :-   Udemy course  


Ques:-   

DevOps team want to deploy a static website on Kubernetes cluster. They are going to use Nginx, phpfpm and MySQL for the database. The team had already gathered the requirements and now they want to make this website live. Below you can find more details:

Create some secrets for MySQL.

Create a secret named mysql-root-pass with key/value pairs as below:

name: password

value: R00t

Create a secret named mysql-user-pass with key/value pairs as below:

name: username

value: kodekloud_sam

name: password

value: Rc5C9EyvbU

Create a secret named mysql-db-url with key/value pairs as below:

name: database

value: kodekloud_db5

Create a secret named mysql-host with key/value pairs as below:

name: host

value: mysql-service

Create a config map php-config for php.ini with variables_order = "EGPCS" data.

Create a deployment named lemp-wp.

Create two containers under it. First container must be nginx-php-container using image webdevops/php-nginx:alpine-3-php7 and second container must be mysql-container from image mysql:5.6. Mount php-config configmap in nginx container at /opt/docker/etc/php/php.ini location.

5) Add some environment variables for both containers:

MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD and MYSQL_HOST. Take their values from the secrets you created. Please make sure to use env field (do not use envFrom) to define the name-value pair of environment variables.

6) Create a node port type service lemp-service to expose the web application, nodePort must be 30008.

7) Create a service for mysql named mysql-service and its port must be 3306.

We already have a /tmp/index.php file on jump_host server.

Copy this file into the nginx container under document root i.e /app and replace the dummy values for mysql related variables with the environment variables you have set for mysql related parameters. Please make sure you do not hard code the mysql related details in this file, you must use the environment variables to fetch those values.


Ans:-

Here is your generated Kubernetes YAML manifest for deploying the LEMP stack with:

  • Secrets for MySQL credentials
  • A php-config ConfigMap
  • A lemp-wp Deployment with two containers (Nginx+PHP and MySQL)
  • Environment variables sourced from secrets
  • Services for both the web app and MySQL


raj@jumphost ~$ cat pod.yaml


---

apiVersion: v1

kind: Secret

metadata:

  name: mysql-root-pass

type: Opaque

stringData:

  password: R00t


---

apiVersion: v1

kind: Secret

metadata:

  name: mysql-user-pass

type: Opaque

stringData:

  username: kodekloud_sam

  password: Rc5C9EyvbU


---

apiVersion: v1

kind: Secret

metadata:

  name: mysql-db-url

type: Opaque

stringData:

  database: kodekloud_db5


---

apiVersion: v1

kind: Secret

metadata:

  name: mysql-host

type: Opaque

stringData:

  host: mysql-service


---

apiVersion: v1

kind: ConfigMap

metadata:

  name: php-config

data:

  php.ini: |

    variables_order = "EGPCS"


---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: lemp-wp

spec:

  replicas: 1

  selector:

    matchLabels:

      app: lemp-wp

  template:

    metadata:

      labels:

        app: lemp-wp

    spec:

      containers:

        - name: nginx-php-container

          image: webdevops/php-nginx:alpine-3-php7

          volumeMounts:

            - name: php-config-volume

              mountPath: /opt/docker/etc/php/php.ini

              subPath: php.ini

            - name: app-volume

              mountPath: /app

          env:

            - name: MYSQL_ROOT_PASSWORD

              valueFrom:

                secretKeyRef:

                  name: mysql-root-pass

                  key: password

            - name: MYSQL_DATABASE

              valueFrom:

                secretKeyRef:

                  name: mysql-db-url

                  key: database

            - name: MYSQL_USER

              valueFrom:

                secretKeyRef:

                  name: mysql-user-pass

                  key: username

            - name: MYSQL_PASSWORD

              valueFrom:

                secretKeyRef:

                  name: mysql-user-pass

                  key: password

            - name: MYSQL_HOST

              valueFrom:

                secretKeyRef:

                  name: mysql-host

                  key: host

        - name: mysql-container

          image: mysql:5.6

          env:

            - name: MYSQL_ROOT_PASSWORD

              valueFrom:

                secretKeyRef:

                  name: mysql-root-pass

                  key: password

            - name: MYSQL_DATABASE

              valueFrom:

                secretKeyRef:

                  name: mysql-db-url

                  key: database

            - name: MYSQL_USER

              valueFrom:

                secretKeyRef:

                  name: mysql-user-pass

                  key: username

            - name: MYSQL_PASSWORD

              valueFrom:

                secretKeyRef:

                  name: mysql-user-pass

                  key: password

            - name: MYSQL_HOST

              valueFrom:

                secretKeyRef:

                  name: mysql-host

                  key: host

      volumes:

        - name: php-config-volume

          configMap:

            name: php-config

        - name: app-volume

          emptyDir: {}


---

apiVersion: v1

kind: Service

metadata:

  name: lemp-service

spec:

  type: NodePort

  selector:

    app: lemp-wp

  ports:

    - protocol: TCP

      port: 80

      targetPort: 80

      nodePort: 30008


---

apiVersion: v1

kind: Service

metadata:

  name: mysql-service

spec:

  selector:

    app: lemp-wp

  ports:

    - protocol: TCP

      port: 3306

      targetPort: 3306



raj@jumphost ~$ kubectl apply -f pod.yaml 

secret/mysql-root-pass created

secret/mysql-user-pass created

secret/mysql-db-url created

secret/mysql-host created

configmap/php-config created

deployment.apps/lemp-wp created

service/lemp-service created

service/mysql-service created


raj@jumphost ~$ kubectl get all

NAME                           READY   STATUS              RESTARTS   AGE

pod/lemp-wp-7c544f45f5-zdwbs   2/2     Running              0          15s


NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE

service/kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP        14m

service/lemp-service    NodePort    10.96.206.255   <none>        80:30008/TCP   15s

service/mysql-service   ClusterIP   10.96.66.225    <none>        3306/TCP       15s


NAME                      READY   UP-TO-DATE   AVAILABLE   AGE

deployment.apps/lemp-wp   1/1     1            1           15s


NAME                                 DESIRED   CURRENT   READY   AGE

replicaset.apps/lemp-wp-7c544f45f5   1         1         1       15s


Manually modify index.php on your jump host: Replace hardcoded MySQL values with environment variable references like this:

raj@jumphost ~$ cd /tmp

raj@jumphost /tmp$ cat index2.php


<?php

$dbname = getenv("MYSQL_DATABASE");

$dbuser = getenv("MYSQL_USER");

$dbpass = getenv("MYSQL_PASSWORD");

$dbhost = getenv("MYSQL_HOST");


$connect = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);


if (!$connect) {

    die("Connection failed: " . mysqli_connect_error());

}


$test_query = "SHOW TABLES FROM $dbname";

$result = mysqli_query($connect, $test_query);


if (!$result) {

    die("Query failed: " . mysqli_error($connect));

}


echo "Connected successfully and query executed.";

?>


Copy the file into the running container:

raj@jumphost /tmp$ kubectl cp index2.php lemp-wp-7c544f45f5-zdwbs:/app -c nginx-php-container


raj@jumphost /tmp$ kubectl exec -it lemp-wp-7c544f45f5-zdwbs -c nginx-php-container -- bash


bash-4.3# cd /app

bash-4.3# pwd

/app

bash-4.3# ls

index2.php

bash-4.3# mv index2.php index.php

bash-4.3# ls

index.php

bash-4.3# 




Conclusion:-

In this hands-on DevOps tutorial, you'll learn how to deploy a secure LEMP stack (Linux, Nginx, MySQL, PHP) on a Kubernetes cluster. This course walks you through:

  • Creating and managing Kubernetes Secrets for MySQL credentials
  • Using ConfigMaps to configure PHP settings
  • Deploying a multi-container Deployment with Nginx, PHP-FPM, and MySQL
  • Injecting secrets into containers using environment variables
  • Mounting configuration files into containers
  • Exposing your static website using a NodePort service
  • Dynamically configuring your PHP application using environment variables

By the end of this video, you'll have a fully functional and secure LEMP-based static website running on Kubernetes — a must-have skill for any aspiring DevOps engineer.

Securely Store and Use License Keys in Kubernetes with Secrets and Volume Mounts

Our course you can check :-   Udemy course  


Ques:-  

DevOps team is working to deploy some tools in Kubernetes cluster. Some of the tools are licence based so that licence information needs to be stored securely within Kubernetes cluster. Therefore, the team wants to utilize Kubernetes secrets to store those secrets. Below you can find more details about the requirements:

We already have a secret key file blog.txt under /opt location on jump host. Create a generic secret named blog, it should contain the password/license-number present in blog.txt file.

Also create a pod named secret-datacenter.

Configure pod's spec as container name should be secret-container-datacenter, image should be debian with latest tag (remember to mention the tag with image). Use sleep command for container so that it remains in running state. Consume the created secret and mount it under /opt/demo within the container.

To verify you can exec into the container secret-container-datacenter, to check the secret key under the mounted path /opt/demo. 


Ans:-

Here’s the complete Kubernetes YAML manifest to meet your requirements:


Assuming you're on the jump host and blog.txt contains the license/password:


raj@jumphost ~$ cat /opt/blog.txt

5ecur3


This creates a secret named blog with a key blog and the value being the contents of blog.txt.


raj@jumphost ~$ kubectl create secret generic blog --from-file=blog=/opt/blog.txt

secret/blog created


Pod Definition with Secret Mounted


raj@jumphost ~$ cat pod.yaml


apiVersion: v1

kind: Pod

metadata:

  name: secret-datacenter

spec:

  containers:

    - name: secret-container-datacenter

      image: debian:latest

      command: ["sleep", "3600"]

      volumeMounts:

        - name: blog-secret

          mountPath: /opt/demo

          readOnly: true

  volumes:

    - name: blog-secret

      secret:

        secretName: blog


raj@jumphost ~$ kubectl apply -f pod.yaml

pod/secret-datacenter created


raj@jumphost ~$ kubectl get all

NAME                    READY   STATUS    RESTARTS   AGE

pod/secret-datacenter   1/1     Running   0          9s


Once the pod is running, you can verify the secret is mounted:


raj@jumphost ~$ kubectl exec -it secret-datacenter -- bash


Inside the container:You should see the contents of blog.txt.


root@secret-datacenter:/# cat /opt/demo/blog

5ecur3

root@secret-datacenter:/# 


Conclusion:-

In this hands-on tutorial, you'll learn how to securely manage sensitive data like license keys or passwords in a Kubernetes cluster using Kubernetes Secrets. We’ll walk through:

  • Creating a generic secret from a license key file (blog.txt)
  • Deploying a pod (secret-datacenter) with a Debian container
  • Mounting the secret as a volume inside the container at /opt/demo
  • Verifying the secret inside the running container

This video is ideal for DevOps engineers and Kubernetes practitioners who want to learn best practices for handling confidential data in containerized environments.


Saturday, 18 October 2025

Deploying a Web App with Persistent Storage in Kubernetes: PV, PVC, Pod & NodePort Explained

Our course you can check :-   Udemy course  


Ques:- 

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 PersistentVolume named as pv-xfusion. Configure the spec as storage class should be manual, set capacity to 3Gi, set access mode to ReadWriteOnce, volume type should be hostPath and set path to /mnt/security .

Create a PersistentVolumeClaim named as pvc-xfusion. Configure the spec as storage class should be manual, request 3Gi of the storage, set access mode to ReadWriteOnce.

Create a pod named as pod-xfusion, mount the persistent volume you created with claim name pvc-xfusion at document root of the web server, the container within the pod should be named as container-xfusion using image nginx with latest tag only (remember to mention the tag i.e nginx:latest).

Create a node port type service named web-xfusion using node port 30008 to expose the web server running within the pod.


Ans:-

Here's a complete Kubernetes YAML template that fulfills all the requirements you've listed:


Note:- We have used Init container for:-

This container will automatically create an index.html file inside the mounted volume before the Nginx container starts.
 

  • The initContainer runs first and writes an HTML file to the volume.
  • The Nginx container then serves this file from /usr/share/nginx/html.

  • ---

    #PersistentVolume (pv-xfusion)

    apiVersion: v1

    kind: PersistentVolume

    metadata:

      name: pv-xfusion

    spec:

      storageClassName: manual

      capacity:

        storage: 3Gi

      accessModes:

        - ReadWriteOnce

      hostPath:

        path: /mnt/security


    ---

    #PersistentVolumeClaim (pvc-xfusion)

    apiVersion: v1

    kind: PersistentVolumeClaim

    metadata:

      name: pvc-xfusion

    spec:

      storageClassName: manual

      accessModes:

        - ReadWriteOnce

      resources:

        requests:

          storage: 3Gi


    ---

    #Pod (pod-xfusion) with volume mounted at web server root and Init container 

    apiVersion: v1

    kind: Pod

    metadata:

      name: pod-xfusion

      labels:

        app: pod-xfusion

    spec:

      initContainers:

        - name: init-xfusion

          image: busybox

          command: ['sh', '-c', 'echo "<h1>Hello from Raj Gupta!</h1>" > /mnt/security/index.html']

          volumeMounts:

            - mountPath: /mnt/security

              name: xfusion-storage

      containers:

        - name: container-xfusion

          image: nginx:latest

          volumeMounts:

            - mountPath: /usr/share/nginx/html

              name: xfusion-storage

      volumes:

        - name: xfusion-storage

          persistentVolumeClaim:

            claimName: pvc-xfusion


    ---

    #NodePort Service (web-xfusion) on port 30008

    apiVersion: v1

    kind: Service

    metadata:

      name: web-xfusion

    spec:

      type: NodePort

      selector:

        # Ensure this matches the pod's labels if you add any

        # For now, we assume no labels, so selector will match pod name

        # Alternatively, you can add a label to the pod and match it here

        app: pod-xfusion

      ports:

        - protocol: TCP

          port: 80

          targetPort: 80

          nodePort: 30008


    Conclusion:-

    Learn how to deploy a web application on a Kubernetes cluster using PersistentVolumes and PersistentVolumeClaims to store application data. This hands-on tutorial walks you through:

    • Creating a PersistentVolume (pv-xfusion) using hostPath
    • Defining a PersistentVolumeClaim (pvc-xfusion) with storage class manual
    • Deploying an Nginx pod (pod-xfusion) with volume mounted at the web server root
    • Automating content creation using an initContainer
    • Exposing the pod using a NodePort service (web-xfusion) on port 30008

    By the end of this video, you'll understand how to manage persistent storage and expose services in Kubernetes effectively.

    Thursday, 16 October 2025

    Kubernetes Deployment Update & Rollback: Real-World DevOps Scenario

    Our course you can check :-   Udemy course  


    Ques:-   

    There is a production deployment planned for next week. DevOps team wants to test the deployment update and rollback on Dev environment first so that they can identify the risks in advance. Below you can find more details about the plan they want to execute.

    Create a namespace datacenter. Create a deployment called httpd-deploy under this new namespace, It should have one container called httpd, use httpd:2.4.28 image and 3 replicas. The deployment should use RollingUpdate strategy with maxSurge=1, and maxUnavailable=2. Also create a NodePort type service named httpd-service and expose the deployment on nodePort: 30008.

    Now upgrade the deployment to version httpd:2.4.43 using a rolling update.

    Finally, once all pods are updated undo the recent update and roll back to the previous/original version.


    Ans:-

    Here’s a step-by-step guide to help you execute the deployment update and rollback scenario in your Dev Kubernetes environment:

    Step 1: Create Namespace
    raj@jumphost ~$ kubectl create namespace datacenter
    namespace/datacenter created

    Step 2: Create Deployment httpd-deploy and service
    raj@jumphost ~$ cat pod.yaml

    ---
    # Save this as httpd-deploy.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpd-deploy
      namespace: datacenter
    spec:
      replicas: 3
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxSurge: 1
          maxUnavailable: 2
      selector:
        matchLabels:
          app: httpd
      template:
        metadata:
          labels:
            app: httpd
        spec:
          containers:
          - name: httpd
            image: httpd:2.4.28
            ports:
            - containerPort: 80

    ---
    # Save this as httpd-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: httpd-service
      namespace: datacenter
    spec:
      type: NodePort
      selector:
        app: httpd
      ports:
      - port: 80
        targetPort: 80
        nodePort: 30008

    raj@jumphost ~$ kubectl apply -f pod.yaml
    deployment.apps/httpd-deploy created
    service/httpd-service created

    raj@jumphost ~$ kubectl describe deploy httpd-deploy -n datacenter | grep -i image
        Image:         httpd:2.4.28

    Step 3: Upgrade Deployment to httpd:2.4.43
    raj@jumphost ~$ kubectl set image deployment/httpd-deploy httpd=httpd:2.4.43 -n datacenter
    deployment.apps/httpd-deploy image updated

    raj@jumphost ~$ kubectl describe deploy httpd-deploy -n datacenter | grep -i image
        Image:         httpd:2.4.43

    You can monitor the rollout status:
    raj@jumphost ~$ kubectl rollout status deployment/httpd-deploy -n datacenter
    deployment "httpd-deploy" successfully rolled out

    Step 4: Rollback to Previous Version
    raj@jumphost ~$ kubectl rollout undo deployment/httpd-deploy -n datacenter
    deployment.apps/httpd-deploy rolled back

    raj@jumphost ~$ kubectl describe deploy httpd-deploy -n datacenter | grep -i image
        Image:         httpd:2.4.28

    Optional: Verify Revision History
    raj@jumphost ~$ kubectl rollout history deployment/httpd-deploy -n datacenter
    deployment.apps/httpd-deploy 
    REVISION  CHANGE-CAUSE
    2         <none>
    3         <none>

    raj@jumphost ~$ 


    Here’s a detailed explanation of the Kubernetes deployment strategy block:


    strategy:
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1
        maxUnavailable: 2

    This configuration defines how Kubernetes updates the pods during a deployment change (like upgrading the image version). Let’s break it down:


    🔄 type: RollingUpdate

    This tells Kubernetes to use a rolling update strategy, which means:

    • Pods are updated gradually, not all at once.
    • It ensures high availability during the update.
    • Old pods are terminated only after new pods are up and running.

    ⚙️ rollingUpdate Parameters

    maxSurge: 1

    • This defines the maximum number of extra pods that can be created above the desired number of replicas during the update.
    • In your case, the deployment has 3 replicas, so during the update, Kubernetes can temporarily run up to 4 pods (3 existing + 1 new).
    • This helps speed up the update while maintaining availability.

    maxUnavailable: 2

    • This defines the maximum number of pods that can be unavailable during the update.
    • In your case, up to 2 pods can be taken down at a time while new ones are being created.
    • This allows Kubernetes to replace pods faster, but it also means only 1 pod will be serving traffic during the update.

    📊 How It Works Together

    Let’s say you’re updating from httpd:2.4.28 to httpd:2.4.43:

    1. Kubernetes starts by creating 1 new pod (due to maxSurge: 1).
    2. It then terminates up to 2 old pods (due to maxUnavailable: 2).
    3. This cycle continues until all old pods are replaced with new ones.
    4. At any point, there will be at least 1 pod running, ensuring minimal downtime.

    🛡️ Why This Matters in Production

    • maxSurge helps maintain availability by adding new pods before removing old ones.
    • maxUnavailable controls how many pods can be offline, balancing speed and reliability.
    • This strategy is ideal for critical services where zero downtime is preferred but fast rollout is also needed.

    Conclusion:-

    Learn how to safely test and execute deployment updates and rollbacks in Kubernetes using a real-world DevOps scenario. In this hands-on tutorial, we’ll walk through:

    • Creating a dedicated namespace for testing
    • Deploying an httpd application with a rolling update strategy
    • Exposing the deployment via a NodePort service
    • Performing a version upgrade using kubectl set image
    • Rolling back to the previous version without breaking revision history

    This video is ideal for DevOps engineers, SREs, and Kubernetes learners who want to master deployment strategies and risk mitigation techniques before production releases.

    Wednesday, 15 October 2025

    Deploying a Guestbook Application on Kubernetes: Redis & Frontend Setup Explained

    Our course you can check :-   Udemy course  


    Ques:-  

    Application development team has finished development of one of the applications and it is ready for deployment. It is a guestbook application that will be used to manage entries for guests/visitors. As per discussion with the DevOps team, they have finalized the infrastructure that will be deployed on Kubernetes cluster. Below you can find more details about it.

    BACK-END TIER

    Create a deployment named redis-master for Redis master.

    a.) Replicas count should be 1.

    b.) Container name should be master-redis-datacenter and it should use image redis.

    c.) Request resources as CPU should be 100m and Memory should be 100Mi.

    d.) Container port should be redis default port i.e 6379.

    Create a service named redis-master for Redis master. Port and targetPort should be Redis default port i.e 6379.

    Create another deployment named redis-slave for Redis slave.

    a.) Replicas count should be 2.

    b.) Container name should be slave-redis-datacenter and it should use gcr.io/google_samples/gb-redisslave:v3 image.

    c.) Requests resources as CPU should be 100m and Memory should be 100Mi.

    d.) Define an environment variable named GET_HOSTS_FROM and its value should be dns.

    e.) Container port should be Redis default port i.e 6379.

    Create another service named redis-slave. It should use Redis default port i.e 6379.

    FRONT END TIER

    Create a deployment named frontend.

    a.) Replicas count should be 3.

    b.) Container name should be php-redis-datacenter and it should use gcr.io/google-samples/gb-frontend@sha256:a908df8486ff66f2c4daa0d3d8a2fa09846a1fc8efd65649c0109695c7c5cbff image.

    c.) Request resources as CPU should be 100m and Memory should be 100Mi.

    d.) Define an environment variable named as GET_HOSTS_FROM and its value should be dns.

    e.) Container port should be 80.

    Create a service named frontend. Its type should be NodePort, port should be 80 and its nodePort should be 30009.


    Ans:-

    Here's the complete set of Kubernetes YAML manifests for deploying the guestbook application as described:


    raj@jumphost ~$ cat pod.yaml


    ---

    #Redis Master Deployment

    apiVersion: apps/v1

    kind: Deployment

    metadata:

      name: redis-master

    spec:

      replicas: 1

      selector:

        matchLabels:

          app: redis-master

      template:

        metadata:

          labels:

            app: redis-master

        spec:

          containers:

          - name: master-redis-datacenter

            image: redis

            resources:

              requests:

                cpu: "100m"

                memory: "100Mi"

            ports:

            - containerPort: 6379


    ---

    #Redis Master Service

    apiVersion: v1

    kind: Service

    metadata:

      name: redis-master

    spec:

      selector:

        app: redis-master

      ports:

      - port: 6379

        targetPort: 6379


    ---

    #Redis Slave Deployment

    apiVersion: apps/v1

    kind: Deployment

    metadata:

      name: redis-slave

    spec:

      replicas: 2

      selector:

        matchLabels:

          app: redis-slave

      template:

        metadata:

          labels:

            app: redis-slave

        spec:

          containers:

          - name: slave-redis-datacenter

            image: gcr.io/google_samples/gb-redisslave:v3

            resources:

              requests:

                cpu: "100m"

                memory: "100Mi"

            env:

            - name: GET_HOSTS_FROM

              value: "dns"

            ports:

            - containerPort: 6379


    ---

    #Redis Slave Service

    apiVersion: v1

    kind: Service

    metadata:

      name: redis-slave

    spec:

      selector:

        app: redis-slave

      ports:

      - port: 6379

        targetPort: 6379


    ---

    #Frontend Deployment

    apiVersion: apps/v1

    kind: Deployment

    metadata:

      name: frontend

    spec:

      replicas: 3

      selector:

        matchLabels:

          app: frontend

      template:

        metadata:

          labels:

            app: frontend

        spec:

          containers:

          - name: php-redis-datacenter

            image: gcr.io/google-samples/gb-frontend@sha256:a908df8486ff66f2c4daa0d3d8a2fa09846a1fc8efd65649c0109695c7c5cbff

            resources:

              requests:

                cpu: "100m"

                memory: "100Mi"

            env:

            - name: GET_HOSTS_FROM

              value: "dns"

            ports:

            - containerPort: 80


    ---

    #Frontend Service

    apiVersion: v1

    kind: Service

    metadata:

      name: frontend

    spec:

      type: NodePort

      selector:

        app: frontend

      ports:

      - port: 80

        targetPort: 80

        nodePort: 30009


    raj@jumphost ~$ kubectl apply -f pod.yaml 

    deployment.apps/redis-master created

    service/redis-master created

    deployment.apps/redis-slave created

    service/redis-slave created

    deployment.apps/frontend created

    service/frontend created


    raj@jumphost ~$ kubectl get all

    NAME                                READY   STATUS    RESTARTS   AGE

    pod/frontend-6bb8bc956b-7gjmg       1/1     Running   0          64s

    pod/frontend-6bb8bc956b-hqkj5       1/1     Running   0          64s

    pod/frontend-6bb8bc956b-ptgnf       1/1     Running   0          64s

    pod/redis-master-686856dd49-qf9lh   1/1     Running   0          64s

    pod/redis-slave-668f85fd-bbqgj      1/1     Running   0          64s

    pod/redis-slave-668f85fd-pfd49      1/1     Running   0          64s


    NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE

    service/frontend       NodePort    10.96.151.213   <none>        80:30009/TCP   64s

    service/kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP        21m

    service/redis-master   ClusterIP   10.96.207.252   <none>        6379/TCP       64s

    service/redis-slave    ClusterIP   10.96.17.4      <none>        6379/TCP       64s


    NAME                           READY   UP-TO-DATE   AVAILABLE   AGE

    deployment.apps/frontend       3/3     3            3           64s

    deployment.apps/redis-master   1/1     1            1           64s

    deployment.apps/redis-slave    2/2     2            2           64s


    NAME                                      DESIRED   CURRENT   READY   AGE

    replicaset.apps/frontend-6bb8bc956b       3         3         3       64s

    replicaset.apps/redis-master-686856dd49   1         1         1       64s

    replicaset.apps/redis-slave-668f85fd      2         2         2       64s

    raj@jumphost ~$ 


    Conclusion:-

    Learn how to deploy a complete guestbook application on a Kubernetes cluster using Redis for backend data storage and a PHP-based frontend. This hands-on tutorial walks you through creating deployments and services for Redis master, Redis slave, and the frontend application. You'll understand how to configure container resources, environment variables, and expose services using NodePort.

    ✅ What you'll learn:

    • Creating Kubernetes deployments for Redis master and slave
    • Setting resource requests for containers
    • Defining environment variables in pods
    • Exposing services with ClusterIP and NodePort
    • Deploying a frontend application connected to Redis

    Perfect for DevOps engineers, SREs, and developers looking to gain practical experience with Kubernetes application deployment.