DEV Community

lechat
lechat

Posted on

Create a k8s development environment by Minikube

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 
Enter fullscreen mode Exit fullscreen mode

Then you get minikube version

minikube version: v1.35.0 commit: dd5d320e41b5451cdf3c01891bc4e13d189586ed-dirty 
Enter fullscreen mode Exit fullscreen mode

In the same way, please check git version too:

git -v git version 2.47.1.windows.2 
Enter fullscreen mode Exit fullscreen mode

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

cd Documents mkdir projects cd projects mkdir test-project cd test-project 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Create pods

Let's create PHP, Nginx, MariaDB environment.

At first, enable ingress:

minikube addons enable ingress 
Enter fullscreen mode Exit fullscreen mode

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: | <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html <Directory "/var/www/html"> Options Indexes FollowSymLinks AllowOverride All Require all granted DirectoryIndex index.html index.php </Directory> ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined </VirtualHost> --- # 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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Now our environment is ready to go!

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

mkdir mounttest minikube mount "$Home\\Documents\\projects\\test-project\\mounttest:/mnt/project" 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Or check the standard output:

kubectl logs php-app-bf6f77579-454jl 
Enter fullscreen mode Exit fullscreen mode

To see cluster-wide information:

kubectl get events --sort-by='.metadata.creationTimestamp' 
Enter fullscreen mode Exit fullscreen mode

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

minikube tunnel 
Enter fullscreen mode Exit fullscreen mode

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 <none> localhost 192.168.59.100 80 103s 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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!"; 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Why did it return 404 at first, but started working after changing the IP from localhost to 192.168.59.100.nip.io?

In the initial Ingress definition inside the .yaml file, it was configured like this:

rules: - host: localhost 
Enter fullscreen mode Exit fullscreen mode

This means the Ingress rule is set to only handle requests where the hostname is localhost.

However, the virtual network created by Minikube (e.g., 192.168.59.100) exists outside the host OS's localhost.
So when you access 192.168.59.100 in your browser, the Ingress controller:

  • sees that the hostname doesn't match (localhost ≠ 192.168.59.100)
  • finds no matching rule
  • and returns a 404 error

So why does it work when you change to 192.168.59.100.nip.io?

When you change the rule like this:

rules: - host: 192.168.59.100.nip.io 
Enter fullscreen mode Exit fullscreen mode

Ingress recognizes the hostname and thinks:

"Ah, this is the hostname I'm in charge of!"

As a result, it correctly applies the routing rule.

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: | <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html <Directory "/var/www/html"> Options Indexes FollowSymLinks AllowOverride All Require all granted DirectoryIndex index.html index.php </Directory> ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined </VirtualHost> --- # 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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Check minikube IP by:

minikube ip 
Enter fullscreen mode Exit fullscreen mode

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" 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)