Kubefeeds Team A dedicated and highly skilled team at Kubefeeds, driven by a passion for Kubernetes and Cloud-Native technologies, delivering innovative solutions with expertise and enthusiasm.

Create a k8s development environment by Minikube

6 min read

Prerequisites

My environment is Windows10.

Download tools

Please install minikube from here:

https://minikube.sigs.k8s.io/docs/star

Let’s use virtualbox driver for minikube.

Please install virtualbox from here:

http://virtualbox.org/wiki/Downloads

We will use Git too. Please install Git from here:

https://git-scm.com/downloads

Let’s install skaffold too:

https://skaffold.dev/docs/install/

Then please install kubernetes too:

https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/

(If you use choco or scoop, it is easy to install)

After installing them, let’s confirm if you can access minikube from Powershell.
Open Powershell then run the following command:

minikube version

Then you get minikube version

minikube version: v1.35.0
commit: dd5d320e41b5451cdf3c01891bc4e13d189586ed-dirty

In the same way, please check git version too:

git -v
git version 2.47.1.windows.2

Now let’s make a test project for this test.

cd Documents
mkdir projects
cd projects
mkdir test-project
cd test-project

Minikube start (minikube cluster)

terminology

What are Containers?

Containers are lightweight, isolated runtime environments that package an application and its dependencies. They ensure consistency across different computing environments.

What are Pods?

A Pod is the smallest deployable unit in Kubernetes, acting as a wrapper for one or more containers.

What is a Cluster?

A Cluster is the highest-level component in Kubernetes, consisting of multiple worker nodes that run Pods.

Start a minikube cluster

Please run this command:

minikube start --driver=virtualbox

Then minikube will start. Please note that, if it is docker driver, you can use mount option like

minikube start --mount --mount-string="$HOME:/src" --driver=docker

but unfortunately this is impossible with virtualbox driver. (but virtualbox is free unlike docker desktop…)

Now let’s install kubernetes. But we will just install kubernetes via minikube:

minikube kubectl -- version

After kubernetes is installed, kubernetes version is displayed like:

minikube kubectl -- version
Client Version: v1.32.0
Kustomize Version: v5.5.0
Server Version: v1.32.0

Create pods

Let’s create PHP, Nginx, MariaDB environment.

At first, enable ingress:

minikube addons enable ingress

Then please save the following as php-mariadb.yaml. This file contains definition of containers in the pods. Please note that you must use “LF” for this file’s linebreaks not “CRLF”:

# MariaDB Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment name
  name: mariadb
  # Labels for identifying the deployment
  labels:
    app: mariadb
spec:
  # Number of MariaDB instances
  replicas: 1
  selector:
    # Matching labels for pods
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
        - name: mariadb
          # Docker image for MariaDB
          image: mariadb:11.3
          # Port on which MariaDB runs
          ports:
            - containerPort: 3306
          # Environment variables for MariaDB
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "rootpassword"
            - name: MYSQL_DATABASE
              value: "mydatabase"
            - name: MYSQL_USER
              value: "myuser"
            - name: MYSQL_PASSWORD
              value: "mypassword"
          # Volume mounting for MariaDB data storage
          volumeMounts:
            - name: mariadb-storage
              mountPath: /var/lib/mysql
      # Definition of volumes
      volumes:
        - name: mariadb-storage
          emptyDir: {}
---
# MariaDB Service
apiVersion: v1
kind: Service
metadata:
  # Service name for MariaDB
  name: mariadb
spec:
  ports:
    - port: 3306
  selector:
    # Selector for connecting to MariaDB pods
    app: mariadb
---
# PHP Application Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment name
  name: php-app
  # Labels for identifying the PHP deployment
  labels:
    app: php-app
spec:
  # Number of PHP application instances
  replicas: 1
  selector:
    matchLabels:
      app: php-app
  template:
    metadata:
      labels:
        app: php-app
    spec:
      containers:
        - name: php-app
          # Docker image for PHP application
          image: php:8.3-apache
          # Port on which PHP Apache runs
          ports:
            - containerPort: 80
          # Volume mounting for PHP application code from host machine
          volumeMounts:
            - name: php-app-code
              mountPath: /var/www/html
            - name: apache-config
              mountPath: /etc/apache2/sites-enabled/000-default.conf
              subPath: default
      # Definition of volumes
      volumes:
        - name: php-app-code
          hostPath:
            path: "/mnt/project"
            type: Directory
        - name: apache-config
          configMap:
            name: apache-config
---
# ConfigMap for Apache Configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: apache-config
data:
  default: |
    
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
            DirectoryIndex index.html index.php
        
        ErrorLog /var/log/apache2/error.log
        CustomLog /var/log/apache2/access.log combined
    
---
# PHP Application Service
apiVersion: v1
kind: Service
metadata:
  # Service name for PHP application
  name: php-app-service
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
  selector:
    # Selector for connecting to PHP pods
    app: php-app
---
# Ingress resource for PHP Application
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: php-app-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: localhost
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: php-app-service
                port:
                  number: 80

Then please save the following as skaffold.yaml:

apiVersion: skaffold/v4beta12
kind: Config
metadata:
  name: php-mariadb-app
deploy:
  kubectl: {}
manifests:
  rawYaml:
    - php-mariadb.yaml

Now our environment is ready to go!

Before starting our environment, let’s create a mount directory:

mkdir mounttest
minikube mount "$HomeDocumentsprojectstest-projectmounttest:/mnt/project"

This mount command will keep running. For subsequent commands, please open a new terminal and run the commands on it.

Please run this command (please make sure you in the test-project directory):

cd {the test-project directory}
skaffold delete
skaffold run --force

This command will keep running. For subsequent commands, please open a new terminal and run the commands on it.

Please wait a while… Your kubernetes environment is being created in the minikube cluster. This will take time.

See here for more skaffold examples: https://github.com/GoogleContainerTools/skaffold/tree/main/examples

For available paramters: https://skaffold.dev/docs/references/yaml/

Now your php and mariadb should be running. Let’s check it by kubectl get pods:

kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
mariadb-998f96ddb-84kqs   1/1     Running   0          64s
php-app-bf6f77579-454jl   1/1     Running   0          64s

If all of them are “Ready 1/1”, it means your environment is running without problem.

By the way, these are called pods:

mariadb-998f96ddb-84kqs (pod)
php-app-bf6f77579-454jl (pod)

Our containers defined by the yaml file are in the pods. One pod can have multiple containers inside.

Sometimes we define logger container and put them in the pod together with a php container.
Such containers are called “sidecar container.”

If any of the pods is not ready, please check the pod:

kubectl describe pod php-app-bf6f77579-454jl 

Or check the standard output:

kubectl logs php-app-bf6f77579-454jl 

To see cluster-wide information:

kubectl get events --sort-by='.metadata.creationTimestamp'

When the creation is done, to access minikube environment from host, let’s run this command:

minikube tunnel

This command will keep running. For subsequent commands, please open a new terminal and run the commands on it.

Mount /var/www/html to the host PC and access the file through ingress

Now let’s see if you can access the ingress. Run this command and get ingress information:

kubectl get ingress
NAME              CLASS    HOSTS       ADDRESS          PORTS   AGE
php-app-ingress      localhost   192.168.59.100   80      103s

And, following this information, if you access 192.168.59.100 from your browser, you will see 404.

If you can see the 404, you are ready to go further.

Please check the ingress ip by:

kubectl get ingress
NAME              CLASS   HOSTS       ADDRESS          PORTS   AGE
php-app-ingress   nginx   localhost   192.168.59.100   80      93s

My IP is 192.168.59.100, so please update the php-mariadb.yaml’s ingress configuration:

# Ingress resource for PHP Application
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: php-app-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: 192.168.59.100.nip.io #!!! Change here !!!!
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: php-app-service
                port:
                  number: 80

We have mounted our “mounttest” to “/mnt/project” (of minikube) and this “/mnt/project” is mounted again to “/var/www/html” of php-appache pod. So let’s create index.php like this:

<?php
echo "Hello World!";

Please save it in the mounttest of your project folder.

Now open http://192.168.59.100.nip.io/ from your browser. You should see Hello World! If you change the php code, you can see that the change is reflected in real time (if not, please disable opcache from your php.ini). Your PHP code is working in a k8s cluster!

Now you can edit your php in your mounttest folder and the edit is reflected to the php pod in the kubernetes cluster in real time. If you need, you can even set php debug for your development. But, if you with minikube & kubernetes in your local, please don’t forget to disable opcache in your php.ini:

opcache.enable=0
opcache.validate_timestamps=1
opcache.revalidate_freq=0

Use MariaDB from host pc

Let’s change MariaDB’s service:

# MariaDB Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment name
  name: mariadb
  # Labels for identifying the deployment
  labels:
    app: mariadb
spec:
  # Number of MariaDB instances
  replicas: 1
  selector:
    # Matching labels for pods
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
        - name: mariadb
          # Docker image for MariaDB
          image: mariadb:11.3
          # Port on which MariaDB runs
          ports:
            - containerPort: 3306
          # Environment variables for MariaDB
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "rootpassword"
            - name: MYSQL_DATABASE
              value: "mydatabase"
            - name: MYSQL_USER
              value: "myuser"
            - name: MYSQL_PASSWORD
              value: "mypassword"
          # Volume mounting for MariaDB data storage
          volumeMounts:
            - name: mariadb-storage
              mountPath: /var/lib/mysql
      # Definition of volumes
      volumes:
        - name: mariadb-storage
          emptyDir: {}
---
kind: Service
apiVersion: v1
metadata:
  name: mariadb
spec:
  type: NodePort
  selector:
    app: mariadb
  ports:
    - port: 3306
      targetPort: 3306
      protocol: TCP
      nodePort: 30036   # for example (must be within the allowed range, e.g., 30000-32767)
---
# PHP Application Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment name
  name: php-app
  # Labels for identifying the PHP deployment
  labels:
    app: php-app
spec:
  # Number of PHP application instances
  replicas: 1
  selector:
    matchLabels:
      app: php-app
  template:
    metadata:
      labels:
        app: php-app
    spec:
      containers:
        - name: php-app
          # Docker image for PHP application
          image: php:8.3-apache
          # Port on which PHP Apache runs
          ports:
            - containerPort: 80
          # Volume mounting for PHP application code from host machine
          volumeMounts:
            - name: php-app-code
              mountPath: /var/www/html
            - name: apache-config
              mountPath: /etc/apache2/sites-enabled/000-default.conf
              subPath: default
      # Definition of volumes
      volumes:
        - name: php-app-code
          hostPath:
            path: "/mnt/project"
            type: Directory
        - name: apache-config
          configMap:
            name: apache-config
---
# ConfigMap for Apache Configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: apache-config
data:
  default: |
    
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
            DirectoryIndex index.html index.php
        
        ErrorLog /var/log/apache2/error.log
        CustomLog /var/log/apache2/access.log combined
    
---
# PHP Application Service
apiVersion: v1
kind: Service
metadata:
  # Service name for PHP application
  name: php-app-service
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
  selector:
    # Selector for connecting to PHP pods
    app: php-app
---
# Ingress resource for PHP Application
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: php-app-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: 192.168.59.100.nip.io #!!! Change here !!!!
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: php-app-service
                port:
                  number: 80

You can see that the MariaDB service is using NodePort now.

Stop the skaffold run from the terminal where you executed the command (by ctrl+c), then start again by:

cd {the test-project directory}
skaffold delete
skaffold run --force

Check minikube IP by:

minikube ip

It will give you the IP address of minikube. You can use this IP and the NodePort to access the MariaDB.

For me, the IP address was 192.168.59.100
The NodePort is defined as 30036 in the yaml file.

So by using both 192.168.59.100:30036, you can access the MariaDB.
Please note that the environment of MariaDB is configured this way:

- name: MYSQL_ROOT_PASSWORD
  value: "rootpassword"
- name: MYSQL_DATABASE
  value: "mydatabase"
- name: MYSQL_USER
  value: "myuser"
- name: MYSQL_PASSWORD
  value: "mypassword"
Kubefeeds Team A dedicated and highly skilled team at Kubefeeds, driven by a passion for Kubernetes and Cloud-Native technologies, delivering innovative solutions with expertise and enthusiasm.