How to Build Kubernetes Cluster from Scratch

Setting up Kubernetes cluster from scratch

This guide is a full stack solution for micro-service architect beginners who need to find answers to the following questions:

  • How to setup a Kubernetes cluster
  • How to deploy any technology stack (PHP, Apache, MySQL in this example) containers
  • How to build custom docker images and run them in Kubernetes
  • How to organize comfortable CI/CD development environment
  • How to sync files between local PC and Kubernetes running containers

How to Create a Kubernetes Cluster

There are two options here create it manually or use managed Kubernetes service from one of the following providers:

  • Amazon’s Elastic Kubernetes Service (EKS)
  • Microsoft’s Azure Kubernetes Service (AKS)
  • Google Kubernetes Engine (GKE)
  • DigitalOcean Kubernetes (DOKS)
  • Other providers

I choose managed Kubernetes service DOKS because I am an existing customer of DigitalOcean. 

Open this link and click on Get Started. Sign in, choose datacenter region and click on Create Cluster button.

On your local Mac machine install Kubernetes command-line tool kubectl.

brew install kubectl

Then install DigitalOcean command-line tool doctl.

brew install doctl

Download the configuration file of your newly created cluster to ~/.kube directory. In my case the file is kenzap-kubeconfig.yaml.

It is recommended that you follow guide under DigitalOcean account for this step or follow this guide for more details.

Then run:

cd ~/.kube && kubectl --kubeconfig="kenzap-kubeconfig.yaml" get nodes
doctl auth init
doctl kubernetes cluster kubeconfig save kenzap

Now you can execute any kubectl command to manage your cluster directly on your local machiene.

Technology Stack

In this tutorial we use these docker images that are publicly available through docker hub:

  • mysql:8.0 image — used to store all data of our project inside kubernetes cluster.
  • phpmyadmin image — used for mysql data administration and will by exposed to Internet access under subdomain.
  • php-apache image — one universal image that will be deployed into three separate containers responsible for API calls under, cloud account available under subpath and main website that operates under domain.

Deploying Containers in Kubernetes

In order to have a more organized structure let’s first create a working directory under your local machine.

For example, you can call it kubernetes. This folder will keep configuration files and subfolders linked to running docker containers for syncronization purposes.

1. Deploying MySQL Application

On your local machine under kubernetes folder create mysql/config folder. This is where you store configuration yamlfiles.

  • Create mysql password. In terminal generate mysql root password:
echo -n "mysql-root-password" | base64
  • Create mysql/config/secret.yaml file and paste base64 generated password string:

Namespaces are intended for use in environments with many users spread across multiple teams or projects. It is recommended to replace kenzap namepsace with your project name. 

Once you chose your namespace you should use it with every yaml configuration that you can find below and when you run kubectl commands. 

Use kenzap namespce only for test purposes.

  • Store secret in Kubernetes cluster. First go to folder, cd ../mysql/config/ then:
kubectl apply -f secret.yaml
  • Verify secret successfully created under kenzap namespace:
kubectl -n kenzap get secret

Docker containers are ephemeral meaning that data is not persistent. To solve this problem we use PersistentVolume and PersistentVolumeClaim

  • Create mysql/config/volume.yaml file. Note namespace: kenzap  and storage: 3Gi. You may want to change it according to your needs.

Note path: “/tmp” parameter. This is where your data is stored on a physical host machine. 

Ideally you need to ssh to one of the cluster nodes and create a custom folder there (not /tmp) or use external managed volumes that come from your Kubernetes service provider.

  • Deploy mysql volume:
kubectl apply -f volume.yaml
  • Verify volume claim under kenzap namespace:
kubectl -n kenzap describe pvc mysql-volume-claim

A more friendly way to verify your cluster health is by using Kubernetes Dashboard. It is available under your DOKS account. Kubernetes > Your Cluster > Kubernetes Dashboard. 

DOKS — Kubernetes Dashboard Screenshot.

  • Create mysql/config/mysql.yaml file:

Note that we are using latest mysql:8.0 image, instruct this deployment to use recently created mysql-volume-claim andmysql-pass secret through environmental variables. Port 3306 is opened internally within cluster.

  • Run MySQL deployment:
kubectl apply -f mysql.yaml

Go to Kubernetes Dashboard > Deployments and verify your mysql instance is running successfully.

Kubernetes services in our case ensures that connection to the database is restored automaticcally should container has new IP address as a result of failure or redeployment.

  • Create mysql/config/service.yaml file:
  • Launch MySQL service:
kubectl apply -f service.yaml

Congratulation! At this stage your first micro-service should be up an running. 

A more detailed explanation of mysql deployment is available under this guide.

2. Deploying phpMyAdmin Application

Probably an old fashioned and less secure way to manage mysql databases but still very popular in LAMP technology stack world is a phpMyAdmin.

Under kubernetes folder create phpmyadmin/config folder and cd to this folder from terminal.

  • Create phpmyadmin/config/phpmyadmin.yaml file:

This deployment is instructed to open port 80 within cluster and to connect to mysql instance on port 3306 via mysql service using same very mysql secret password created earlier.

  • Run phpMyAdmin deployment:
kubectl apply -f phpmyadmin.yaml

  • Create phpmyadmin/config/service.yaml file:
  • Launch phpMyAdmin Service:
kubectl apply -f service.yaml

At this stage MySQL and phpMyAdmin deployments should be up and running inside Kubernetes cluster. However access from the Internet is not yet available.

3. Deploying Nginx Controller

Accessing Kubernetes Cluster through Nginx Controller.

In order to grant access from the Internet to Kubernetes running deployments and route traffic accordingly you can setup Load Balancer or Ingress controller.

  • Create Nginx controller deployment:
kubectl apply -f
  • Verify deployment:
kubectl get pods -n ingress-nginx
  • Create Nginx service:
kubectl apply -f
  • Getting External IP:
kubectl get -n ingress-nginx service

Returns under EXTERNAL-IP column in my case.

  • Create kubernetes/ingress.yaml file:

Note the rules and domain used. You need to provide your domain name and point it’s DNS A records to clustern’s External IP address:

Setting up DNS A records under PorkBun account.
  • Deploy ingress from kubernetes folder:
kubectl apply -f ingress.yaml

At this stage you should be able to access your cluster’s phpMyAdmin service by opening where is replaced with your domain name.

Current connection configuration is not secure. You may want to setup let’s encrypt SSL certificates with auto renewal option.

4. Building Custom Docker Image

In this example I use webdevops/php-apache docker image that has apache2, latest ubuntu and PHP preinstalled. Let’s copy this image to your docker hub so that it can be used in custom deployments later on.

If not yet installed locally follow this guide to install docker on your machine.

docker pull webdevops/php-apache
docker login --username kenzap
  • Tag image:
docker tag webdevops/php-apache:latest kenzap/kenzap-web
  • Push to your docker hub:
docker push kenzap/kenzap-web

You need this operation to build images locally with custom dockerfile. You will then be able to push customized images to your docker hub account.

Kubernetes cluster will use these images in a similar fashion as mysql and phpmyadmin deployments described previously except that images will be downloaded directly from your repository for production.

5. Deploying Website Image

Now that you have learned some basics about Kubernetes secrets, deployments, services and ingress let’s create next deployment where you can host your website files.

  • In a similar way as before we need to declare a persistent volume. Create kubernetes/site/config/volume.yaml file:
  • In terminal go to ../kubernetes/site/config  folder then:
kubectl apply -f volume.yaml
  • Create kubernetes/site/config/site.yaml file but do not deploy it:
  • Now let’s create a docker /site/config/Dockerfile file:

Now that on top of the default image configurations additional commands are provided. I decided to install packages like php-redis and nano and also copy apache virtual host configurations vhost.conf.

  • You also need to create /site/config/vhost.conf file where inside this file you need to provide your domain name and other configurations:

The last but one is devspace.yaml file. It indicates to use site.yaml and Dockerfile within same very directory. While Dockerfile at the same time points to use vhost.conf file and install additional packages.

  • Create kubernetes/site/config/devspace.yaml file:
  • Now install kubernetes/site/config/service.yaml file:

Run service:

kubectl apply -f service.yaml

Before running deployment command you need to install devspace.

DevSpace is an open-source developer tool for Kubernetes that lets you develop and deploy cloud-native software faster.

  • Install DevSpace for Mac from terminal or learn more here.
curl -s -L "" | sed -nE 's!.*"([^"]*devspace-darwin-amd64)".*!\1!p' | xargs -n 1 curl -L -o devspace && chmod +x devspace;
sudo mv devspace /usr/local/bin;

DevSpace will help with:

  • live debugging
  • docker image building
  • file synchronization between local directory and running containers
  • setup CI/CD development environment

Once the devspace is installed you should be able to deploy applications with the help of this tool.

Run while under kubernetes/site/config/ folder:

devspace deploy -n kenzap

This will compile custom docker image, push it under your docker hub, run site.yaml deployment and instruct kubernetes to pull custom docker image back.

You can start live development mode or simply sync local folders with running site container to publish first site files. I choose 2nd option:

devspace sync -n kenzap --config devspace.yaml

Note that our /site/config/vhost.conf file was pointing to host website files under /var/www/cloud container’s directory. 

You now need to create kubernetes/site/config/cloud folder locally and based on our devspace.yaml configuration file these folders will be linked and synced automatically.

Expected terminal output of devspace sync command

This means that if you create kubernetes/site/cloud/index.php file. It will be automatically uploaded to kubernetes container as  /var/www/cloud/index.php file and can be accessed in live through your domain name.

In a similar fashion you can create other web micro services that run isolated to each other. In my example these are and You simply need to repeat steps described chapter “5. Deploying Website Image” by providing different app selectors, paths and any other relevant configurations.

In order to make this article better please ping me down below with your feedback.