Kubernetes: deploy Laravel the easy way

Published in April 2018

Updated in May 2020

TL;DR: In this article, you will learn the basics of how to deploy a Laravel application in Kubernetes.

Laravel is an excellent framework for developing PHP applications.

Whether you need to prototype a new idea, develop an MVP (Minimum Viable Product) or release a full-fledged enterprise system, Laravel facilitates all of the development tasks and workflows.

How you deal with deploying the application is a different story.

Vagrant is an excellent choice to set up a development environment that mirrors your production environment.

But it's still limited to a single machine.

In production, you will most likely require more than just one web server and database.

And you probably don't have a single app, but multiple apps with different concerns such as an API, a front-end, workers to process batch jobs, etc.

How do you deploy your apps and make sure that they can scale efficiently with your users?

In this article, you will learn how to set up a Laravel application in Kubernetes.

Kubernetes, why and what?

Who has lots of application deployed in production?

Google, of course.

Kubernetes is an open-source tool that was initially born from Google to facilitate a large number of deployments across their infrastructure.

It is good at three things:

Running any type of app (not just PHP). Scheduling deployments across several servers. Being programmable.

Let's have a look at how you can leverage Kubernetes to deploy a Laravel app.

Deploying a Laravel Application to Minikube

You can run Kubernetes on several cloud hosting providers such as Google Cloud Engine (GCP), Amazon Web Services (AWS), Azure.

In this tutorial, you will run the application on Minikube — a tool that makes it easier to run Kubernetes locally.

Similar to Vagrant, Minikube is merely a Virtual Machine that contains a Kubernetes cluster.

The application

I have prepared a simple Laravel application which you can clone from the repository on GitHub.

It is nothing more than a fresh Laravel installation.

Therefore you can follow this tutorial using either the demo application or you can create a new Laravel application.

Let's get started by cloning the project with:

bash git clone https://github.com/learnk8s/laravel-kubernetes-demo.git cd laravel-kubernetes-demo

Before you start

To follow with this demonstration, you will need the following tools installed in your computer:

Are you having problems installing and running these applications on Windows? Check out the article Getting started with Docker and Kubernetes on Windows 10, for a step by step guide.

Packaging Laravel in a container

Kubernetes doesn't know how to deploy Laravel apps.

Or Java.

Or Node.js.

Or any other programming language.

Kubernetes only knows how to deploy containers.

Containers are a Linux feature that is used to limit what a process can do.

When you start a process such as PHP as a container, you can define how much memory and CPU it can use.

Also, you can define what network and filesystem it is allowed to see (and a few more things).

You could use containers isolate and launch several PHP instances on your server.

Just as you use virtual machine to isolate your development environment.

Docker is the most popular tool to create and run containers.

But there are several other options such as LXC, Podman, containerd, etc.

In this tutorial, you will use Docker.

So, as a first step, you should build a Docker image of your application.

An image contains all the file needed to launch the container.

Go ahead and create a Dockerfile (capital "D") in the root of your project:

Dockerfile FROM composer : 1.6.5 as build WORKDIR /app COPY . /app RUN composer install FROM php : 7.1.8 - apache EXPOSE 80 COPY - - from=build /app /app COPY vhost.conf /etc/apache2/sites - available/000 - default.conf RUN chown - R www - data : www - data /app a2enmod rewrite

This Dockerfile has two parts:

In the first part, you install all the application's dependencies.

The second part prepares the webserver with PHP-mod.

A Dockerfile above uses a multi-stage build.

The Dockerfile is just a description of what files should be bundled in the container.

You can execute the instructions and create the Docker image with:

bash docker build -t laravel-kubernetes-demo .

Note the following about this command:

-t laravel-kubernetes-demo defines the name ("tag") of your container — in this case, your container is just called laravel-kubernetes-demo

defines the name ("tag") of your container — in this case, your container is just called . is the location of the Dockerfile and application code — in this case, it's the current directory

The output is a Docker image.

What is a Docker image?

A Docker image is an archive containing all the files that belong to a container.

If you want to test it, you should run the container (and the process inside it).

You can run the container with:

bash docker run -ti \ -p 8080 :80 \ -e APP_KEY = base64:cUPmwHx4LXa4Z25HhzFiWCf7TlQmSqnt98pnuiHmzgY = \ laravel-kubernetes-demo

And the application should be available on http://localhost:8080.

Please note that, with this setup, the container is generic and the APP_KEY is not hardcoded or shared.

Sharing Docker image with a registry

You built and ran the container locally, but how do you make it available to your Kubernetes cluster?

Usually, to share images, you can use a container registry such as Docker Hub or Quay.io.

Container registries are web apps that store container images — like the laravel-kubernetes-demo image that you built earlier.

In this tutorial you will use Docker Hub to upload your containers.

To use Docker Hub, you first have to create a Docker ID.

A Docker ID is your Docker Hub username.

Once you have your Docker ID, you have to authorise Docker to connect to the Docker Hub account:

bash docker login

Before you can upload your image, there is one last thing to do.

Images uploaded to Docker Hub must have a name of the form username/image :

username is your Docker ID

is your Docker ID image is the name of the image

If you wish to rename your image according to this format, run the following command:

bash docker tag laravel-kubernetes-demo < my-username > /laravel-kubernetes-demo

Please replace <my-username> with your Docker ID this time.

Now you can upload your image to Docker Hub:

bash docker push < my-username > /laravel-kubernetes-demo

Your image is now publicly available as <my-username>/laravel-kubernetes-demo on Docker Hub and everybody can download and run it.

To verify this, you can re-run your app, but this time using the new image name.

bash docker run -ti \ -p 8080 :80 \ -e APP_KEY = base64:cUPmwHx4LXa4Z25HhzFiWCf7TlQmSqnt98pnuiHmzgY = \ < my-username > /laravel-kubernetes-demo

Everything should work exactly as before.

The image is now available in the registry.

Anybody who has access to the registry it can use it.

Deploying Laravel in Kubernetes

Now that the application's image is built and available, you can go ahead an deploy it.

You can deploy the container image with:

bash kubectl run laravel-kubernetes-demo \ --restart = Never \ --image = < my-username > /laravel-kubernetes-demo \ --port = 80 \ --env = APP_KEY = base64:cUPmwHx4LXa4Z25HhzFiWCf7TlQmSqnt98pnuiHmzgY =

Let's review the command:

kubectl run laravel-kubernetes-demo deploys an app in the cluster and gives it the name laravel-kubernetes-demo .

deploys an app in the cluster and gives it the name . --restart=Never is used not to restart the app when it crashes.

is used not to restart the app when it crashes. --image=<my-username>/laravel-kubernetes-demo and --port=80 are the name of the image and the port exposed on the container.

Please note that 80 is the port exposed in the container. If you make a mistake, you shouldn't increment the port; you can still use port 80. If you make a mistake, you can execute kubectl delete pod app and start again.

In Kubernetes, an app deployed in the cluster is called a Pod.

You can check that a Pod is successfully created with:

bash kubectl get pods NAME READY STATUS RESTARTS AGE laravel-kubernetes-demo 1 /1 Running 0 18m

You can also use the Minikube dashboard to monitor the pods and cluster.

The GUI also helps with visualising most of the discussed concepts.

To view the dashboard, just run the following:

bash minikube dashboard

or to acquire the dashboard's URL address:

bash minikube dashboard --url = true

Exposing the application

So far, you have only deployed an application.

But how do you access it?

The deployed application has a dynamic IP address assigned.

That means that every time you deploy or scale an app, a different IP address is assigned to it.

You might find it difficult to route the traffic directly to the app.

To avoid updating IP addresses manually when visiting the app, you can use a load balancer.

In Kubernetes, a Service is a load balancer for a collection of Pods.

So even if the IP address of a Pod changes, the IP address of the Service is always fixed.

The Service is designed to keep track of the Pods' IP addresses, so you don't have to update IP address manually.

Pod 1 10.0.0.1 IP Pod 2 10.0.0.2 IP Service 10.0.1.0 IP Incoming traffic

You can create a service with:

bash kubectl expose pods laravel-kubernetes-demo --type = NodePort --port = 80 service "laravel-kubernetes-demo" exposed

You can verify that the Service was created successfully with:

bash kubectl get services

You can also view the running service under the "Services" navigation menu within the dashboard.

A more exciting way to verify this deployment and the service is seeing it in the browser.

To obtain the URL of the application (service), you can use the following command:

bash minikube service --url = true laravel-kubernetes-demo http://192.168.99.101:31399

or, launch the application directly in the browser:

bash minikube service laravel-kubernetes-demo

Breaking the app

At this point you should have a local Kubernetes cluster with:

A single Pod running

A Service that routes traffic to a Pod

Having a single Pod is usually not enough.

For instance, what happens when the Pod is accidentally deleted?

Let's find out.

You can delete the Pod with:

bash kubectl delete pod laravel-kubernetes-demo

If you visit the app with minikube service laravel-kubernetes-demo , does it still work?

It doesn't.

But why?

You deployed a single Pod in isolation.

There's no process looking after and respawning it when it's deleted.

As you can imagine, this deployment is of limited used.

It'd be better if there could be a mechanism to watch Pods and restart them when they are deleted, or they crash.

Kubernetes has an abstraction designed to solve that specific challenge: the Deployment object.

Here's an example for a Deployment definition:

deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : laravel - kubernetes - demo spec : selector : matchLabels : run : laravel - kubernetes - demo template : metadata : labels : run : laravel - kubernetes - demo spec : containers : - name : demo image : <my - username > /laravel - kubernetes - demo ports : - containerPort : 80 env : - name : APP_KEY value : base64 : cUPmwHx4LXa4Z25HhzFiWCf7TlQmSqnt98pnuiHmzgY=

You can save the file above as deployment.yaml .

You can submit the Deployment to the cluster with:

bash kubectl apply -f deployment.yaml

If you try to visit the application with minikube service laravel-kubernetes-demo , do you see the app?

Yes, it worked.

Did the Deployment create a Pod?

Let's find out:

bash kubectl get pods

The Deployment created a single Pod.

What happens when you delete it again?

bash kubectl delete pod < replace with pod id >

The Deployment immediately respawned another Pod.

Great!

Scaling the application

You have successfully deployed the application in Kubernetes that is resilient.

But you still have one deployment with a single Pod running.

What if your application becomes more popular?

Let's scale this deployment to three instances.

Pod 1 Pod 2 Pod 3 Service Incoming traffic

You can use the following command to scale the Deployment:

bash kubectl scale --replicas = 3 deployment/laravel-kubernetes-demo deployment "laravel-kubernetes-demo" scaled

You have three replicas.

You can verify it with:

bash kubectl get deployment,pods NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE laravel-kubernetes-demo 3 3 3 3 59m

You can also see this in the Dashboard under Pods or in the Service detail screen.

Now you're running three instances of the applications using three Pods.

Imagine your application becoming even more popular.

Thousands of visitors are using your website or software.

In the past, you may have been busy writing more scripts to create more instances of your application.

In Kubernetes you can scale to multiple instances in a snap:

bash kubectl scale --replicas = 10 deployment/laravel-kubernetes-demo deployment "laravel-kubernetes-demo" scaled

You can see how convenient it is to use Kubernetes to scale your website.

Using Nginx Ingress to expose the app

You've already achieved great things; you deployed the application and scaled the deployment.

You have already seen the running application in the browser when pointed to the cluster's (Minikube) IP address and node's port number.

Now, you will see how to access the application through an assigned URL as you would do when deploying to the cloud.

To use a URL in Kubernetes, you need an Ingress.

An Ingress is a set of rules to allow inbound connections to reach a Kubernetes cluster.

In the past, you might have used Nginx or Apache as a reverse proxy.

The Ingress is the equivalent of a reverse proxy in Kubernetes.

Pod 1 Pod 2 Pod 3 Service Ingress Incoming traffic

I have included an ingress.yaml file with the source code of this demo application with the following contents:

ingress.yaml apiVersion : networking.k8s.io/v1beta1 kind : Ingress metadata : name : laravel - kubernetes - demo - ingress annotations : ingress.kubernetes.io/rewrite-target : / spec : rules : - http : paths : - path : / backend : serviceName : laravel - kubernetes - demo servicePort : 80

Among the basic content you would expect from a Kubernetes resource file, this file defines a set of rules to follow when routing inbound traffic.

The Ingress resource is useless without an Ingress controller so you will need to create a new controller or use an existing one.

Minikube comes with the Nginx as Ingress controller, and you can enable it with:

bash minikube addons enable ingress

Please note that it may take few minutes for Minikube to download and install Nginx as an Ingress.

Once you have enabled the Ingress addon, you can create the Ingress in this way:

bash kubectl create -f ingress.yaml

You can verify and obtain the Ingress' information by running the following command:

bash kubectl describe ing laravel-kubernetes-demo-ingress Name: laravel-kubernetes-demo-ingress Namespace: default Address: 192.168 .99.101 Default backend: default-http-server:80 ( < none > ) Rules: Host Path Backends ---- ---- -------- * / laravel-kubernetes-demo:80 ( 172.17 .0.6:80 ) Annotations: ingress.kubernetes.io/rewrite-target: / Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 39s nginx-ingress-controller Ingress default/laravel-kubernetes-demo-ingress Normal UPDATE 20s nginx-ingress-controller Ingress default/laravel-kubernetes-demo-ingress

But where do you access the app?

You should visit the IP address of the cluster.

You can use minikube's IP address and visit http://minikube_ip.

This is just the beginning

Hopefully, this article has helped you in getting acquainted with Kubernetes.

From my own experience, once one has performed similar deployments a couple or more times, things start getting habitual and make a lot more sense.

But our Kubernetes journey has only just begun.

That's all folks!

If you enjoyed this article, you might find the following articles interesting:

Kubernetes to control IoT devices such as Raspberry Pis and build your Internet of Things automated fleet.

Learn how you can use virtual machines that can disappear at any time to lower your infrastructure costs