Deploy Laravel Application Using Kubernetes For Improved Web Performance

Deploy Laravel Application Using Kubernetes For Improved Web Performance

Introduction

Laravel is a popular PHP framework that helps you build web applications with different frontend frameworks like React.js. You can also implement a decoupled application by only writing the APIs.

Whether it’s a monolith or API, leverage the power of Kubernetes so your application can perform under high traffic. By taking care of the application availability, Kubernetes efficiently utilizes resources and can scale up or down based on demand.

In this blog, you’ll learn how to deploy a Laravel application using Kubernetes.

Before we dive in, refer to this template I’ve prepared, which will help you with deployment.

Priorities

We will begin by deploying the application in a local Kubernetes cluster using minikube.

With Nginx as the web server, we’ll consider a simple Laravel application that consists of a MySQL database for storing the data.

It is easy to set up if you do not have minikube installed on your machine. Select the options based on your machine's configuration.

image.png

Execute the following command:

minikube start

It will create the cluster in your machine.

We will require a few minikube addons for this exercise.

minikube addons enable ingress
minikube addons enable metrics-server

ingress allows you to route requests from your machine to the cluster, making the application available to you.

metrics-server collects metrics from the pods and exposes them to the Kubernetes API server, which is necessary for pod scheduling and autoscaling.

Restart the minikube cluster once you have enabled the addons.

minikube stop
minikube start

Containerize The Application

ARG PHP_EXTENSIONS="apcu bcmath pdo_mysql redis imagick gd"
FROM thecodingmachine/php:8.1-v4-fpm as php_base
ENV TEMPLATE_PHP_INI=production
WORKDIR /var/www/html
COPY --chown=docker:docker . .
COPY --chown=docker:docker .env.example .env
RUN composer install --optimize-autoloader
RUN php artisan key:generate

The Dockerfile can vary based on your application requirements.

Build the Docker image. It ensures that the image is available to the minikube cluster. minikube runs its Docker environment. Therefore, we must ensure that the deployment happens in the right environment. You can point to the minikube's Docker environment by executing the below in the terminal.

eval $(minikube -p minikube docker-env)

Followed by the command for building an image:

docker build . --tag=ci-k8s

You can choose any image name.

Enter The Kubernetes

Let’s create a few entities in Kubernetes that will deploy the application. You’ll need to make sure it’s scalable and publicly available.

You can prepare the entities one by one. However, we will use helm to prepare the boilerplate entities. It helps in packaging the application deployment and simplifies the deployment.

We will modify those boilerplate entities as per our requirements.

If you don’t have helm, install it from here. Once done, prepare the boilerplate configuration by executing:

helm create chart

It will create a directory named chart with the files like this. Feel free to provide any name while creating the chart.

image.png

You will notice the various entities involved in deploying the application one by one.

Prerequisite

Helm provides a templating feature. What does that mean? It can specify configurations like the number of replicas inside a template file. The entities will use the template to get the values. You will see those template files.

values.yml

The values.yml contains the specifications like the resource you want to give to each pod, the number of pod replicas to maintain, etc. They will vary based on availability factors, cost, and expected traffic.

_helpers.tpl

The _helpers.tpl define templates that you can use in the entities.

Chart.yaml

The Chart.yaml specifies the chart metadata like its name, version, etc.

Now, let's get to the entities.

Volumes

We will require two volumes in this exercise—for codebase and database. In production, you might want to go with database services like AWS RDS, DigitalOcean, etc. Why? They handle the necessary tasks like maintaining backups and allocating resources.

The volume specifications are mentioned in storage.yml.

Configurations

Specifying how to handle PHP requests in Nginx is a must. Let’s use ConfigMap for it. nginx_config.yml contains those specifications.

Deployment

The Deployment entity is responsible for deploying the application and its dependencies. We will have three deployments: phpfpm, Nginx, and database (MySQL). You’ll be able to view them in deployment.yaml.

The nginx deployment relies on the configuration entity.

Services

We will use the Service entity to expose the container ports. The service.yaml specifies how you can do it.

Ingress

The Ingress entity makes the pods publicly available. It routes the traffic from the internet to the pod and vice-versa. The ingress.yaml file explains how you can do it.

Autoscaling

The HorizontalPodAutoscaler entity enables your application to serve under traffic. You can choose between vertical or horizontal scaling of your pods. For this, let’s go with horizontal scaling.

hpa.yaml specifies the threshold and the maximum number of replicas to maintain. The pod scheduler creates a new pod if the resource utilization crosses the threshold.

The database and the phpfpm pods may consume more CPU and memory based on the traffic. Therefore, we have configured the autoscalers for them.

The specifications minReplicas, maxReplicas, and metrics will vary by application and business requirement. Also, consider the resources available in the node.


Once you create all the required entities, the chart directory will look like this:

image.png

Let's see how we can deploy the application.

Deploying The Application

Create The Cluster

minikube start

image.png

Enable Tunneling

It will route the traffic from your machine to the cluster.

minikube tunnel

image.png

It will request authorization as you have exposed port 80.

Install The Chart

Installing the chart will initiate the deployment, and the API server will instantiate the entities. Before installation, replace the database secrets with the actual values. We specified them in values.yaml.

dbRootPassword: "test"
dbName: "test"
dbUser: "test"
dbPassword: "test"

Specify the correct secrets as environment variables.

export DB_ROOT_PASSWORD=rootpass DB_NAME=db DB_USER=main DB_PASSWORD=password API_KEY=samplekey

To validate whether there are issues with the entities, execute the following from the project root:

helm install --set dbRootPassword=$DB_ROOT_PASSWORD --set dbName=$DB_NAME --set dbUser=$DB_USER --set dbPassword=$DB_PASSWORD --set apiKey=$API_KEY laravel-k8s chart/ --dry-run --debug

It should output the entities with the exact specifications. If you see an error, something is wrong.

It is time to install the chart.

helm install --set dbRootPassword=$DB_ROOT_PASSWORD --set dbName=$DB_NAME --set dbUser=$DB_USER --set dbPassword=$DB_PASSWORD --set apiKey=$API_KEY laravel-k8s chart/

laravel-k8s is the name of the chart. You can name it something else.

chart/ is the location of the directory that contains all the entities.

Kubernetes Dashboard

minikube comes with the Kubernetes dashboard. You can open it by executing:

minikube dashboard

It might take time to open. Once it does, you will see:

image.png

It specifies that the pods are starting up. Once the pods are ready, they are green.

image.png

Verify

To check whether the application is running, initiate a curl request from your machine.

curl -v http://localhost/

You should see a 200 OK response.

Wondering how the pods can listen to the requests from your machine? Because you enabled the tunneling and ingress entity. It means your machine's port 80 can communicate with the cluster.

Autoscaling In Action

Let's say the application has an endpoint that does some computational tasks. We will simulate the traffic using ApacheBench.

ab -c 2 -n 5000 http://localhost/api/calc-daily-average-power-consumption

Kubernetes relies on the metrics server to gather data from the pods. Based on the data and the specified threshold, it schedules the pods.

Once you have executed the command, it takes a while for the new pod to come online.

Screenshot 2022-09-05 at 1.05.40 PM.png

Since it can automatically scale, once it sees that the resource consumption in a pod is within the threshold, it will destroy the newly created pod.

Screenshot 2022-09-05 at 1.13.13 PM.png

Debug Incorrect Templates

Helm provides many ways to find the issue in incorrect templates. In this case, execute the below command:

helm template --set dbRootPassword=$DB_ROOT_PASSWORD --set dbName=$DB_NAME --set dbUser=$DB_USER --set dbPassword=$DB_PASSWORD --set apiKey=$API_KEY laravel-k8s chart/ --debug > test.yaml

The YAML content will be output in the test.yaml file. You can find the issue from there.

Conclusion

You learned the power of Kubernetes to make your applications ready to serve traffic and how cost-efficient it is.

It will reduce the number of pods based on traffic. You will choose between EKS, GKE, AKS, etc. when it comes to production.

A few things, like the dashboard, might be different, but most of the processes we undertook in this blog will stay the same. Try it out, and let us know how it went!