DEV Community

Cover image for Deploying a Containerized Laravel Application to a Local Kubernetes Cluster: A Step-by-Step Guide
dami
dami

Posted on

Deploying a Containerized Laravel Application to a Local Kubernetes Cluster: A Step-by-Step Guide

Welcome to this comprehensive step-by-step guide, where we'll begin by unravelling the fundamentals of Containers, Docker, and Kubernetes. Building on this foundational knowledge, we'll then delve into the art of crafting Dockerfiles and configuring Kubernetes manifests.

We will cover the entire process, from preparing the Laravel application for containerization to setting up the local Kubernetes environment using Minikube. By following this guide, readers will gain valuable insights into the containerization process and learn how to leverage Kubernetes for efficient application deployment.

Prerequisites

Before diving into this tutorial, there are a few essentials we need to have in place:

  1. Basic understanding of PHP and the Laravel framework.
  2. Docker Desktop Installed. Here’s the link to download Docker Desktop.
  3. Minikube is required to create a local Kubernetes environment suitable for testing and development. Here’s the link to set it up.
  4. Kubectl is the command-line tool used to interact with Kubernetes clusters. We need it to communicate with our local Minikube cluster. Here’s the link to download it.

Docker, Containers, and Kubernetes: The Basics

Before we dive into this tutorial, let's get acquainted with the essentials of these technologies:

Docker

Docker is a groundbreaking platform that simplifies the packaging, distribution, and deployment of applications. It bundles an app and its dependencies, including libraries and configurations into a container. Docker abstracts away the inconsistencies across different environments, ensuring consistent behaviour regardless of where the application is executed, in essence promoting the "build once, run anywhere" philosophy.

Containers

A container is a lightweight, standalone executable unit that packages an application along with its runtime, system tools, libraries, and settings. Containers provide an isolated environment, isolating the application from its host system and other containers.

Kubernetes

Kubernetes is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. It abstracts the complexity of managing clusters of containers across a multitude of machines, handling tasks such as scaling, load balancing, self-healing, and rolling updates.

Now that we've got these basics down, let's dive into our tutorial. We'll use these tools to put our Laravel app on a local Kubernetes playground!

Getting Started

Let’s start by creating a new Laravel project. To do that, we’ll run the following command in the terminal:



    laravel new sample-app


Enter fullscreen mode Exit fullscreen mode

After installation is complete, verify it by running:



    php artisan serve


Enter fullscreen mode Exit fullscreen mode

The link displayed in the terminal should redirect to Laravel’s welcome page.

Containerizing the Application

To containerize our application, we’ll need to start by creating a Dockerfile. This is the file Docker uses to build an image of our application.

In the root of our application, we’ll create a file titled Dockerfile without any extension. Let's break down the contents of the Dockerfile step by step:

1. Setting the Base Image

We begin by specifying the base image for our container as php:7.4-fpm. This base image equips us with the necessary PHP runtime environment.



    FROM php:7.4-fpm as php


Enter fullscreen mode Exit fullscreen mode

2. Installing PHP Extensions and Dependencies

The next step involves installing essential PHP extensions and dependencies needed by our Laravel application. We update the package list,



    RUN apt-get update \
       && apt-get install -y \
           libzip-dev \
           zlib1g-dev\
           unzip \
       && docker-php-ext-install \
           pdo \
           pdo_mysql \
           sockets \
           zip


Enter fullscreen mode Exit fullscreen mode

3. Clearing Local Repository of Retrieved Package Files

After installing packages, we execute a cleanup to eliminate unnecessary files from the package installation process.



    RUN apt-get clean


Enter fullscreen mode Exit fullscreen mode

4. Setting Up The Application Directory and Files
Here, we create a directory named /app within the image and copy all the application files from your local machine to this directory. This establishes the directory structure needed for the application.



    RUN mkdir /app
    ADD . /app
    WORKDIR /app


Enter fullscreen mode Exit fullscreen mode

5. Installing Composer and Application Dependencies

Composer is an essential dependency management tool for PHP applications. This step installs Composer in the image and copies it to the appropriate location. We then run composer install to install the dependencies specified in the composer.json file of your Laravel application.



    RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
    RUN composer install 


Enter fullscreen mode Exit fullscreen mode

6. Starting the Application

Finally, we define the command that will be executed when the container starts. In this case, we use php artisan serve to launch the Laravel development server. The server listens on all available network interfaces (0.0.0.0) and port 8082.



    CMD php artisan serve --host=0.0.0.0 --port=8082


Enter fullscreen mode Exit fullscreen mode

7. Exposing the Port

To allow external access to the Laravel application running inside the container, we expose port 8082, which corresponds to the port the server is listening on.



    EXPOSE 8082


Enter fullscreen mode Exit fullscreen mode

Here’s the complete Dockerfile.



    # Set the base image for subsequent instructions
    FROM php:7.4-fpm as php
    # Install PHP extensions and dependencies
    RUN apt-get update \
        && apt-get install -y \
            libzip-dev \
            zlib1g-dev\
            unzip \
        && docker-php-ext-install \
            pdo \
            pdo_mysql \
            sockets \
            zip
    # Clear out the local repository of retrieved package files
    RUN apt-get clean
    RUN mkdir /app
    ADD . /app
    WORKDIR /app
    # Install Composer
    RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
    RUN composer install 
    CMD php artisan serve --host=0.0.0.0 --port=8082
    EXPOSE 8082



Enter fullscreen mode Exit fullscreen mode

Building the Docker Image

After writing the Dockerfile, the next step is building the Docker image. We can do that by running the command below in the root directory of our project.



    docker build -t test-image .


Enter fullscreen mode Exit fullscreen mode

This command may take some time to execute, depending on the computer and the internet speed.

After the image is successfully built, we can integrate it into our Kubernetes cluster using the following command:



    minikube image load test-image


Enter fullscreen mode Exit fullscreen mode

Writing the Kubernetes Manifest

Now that we’ve successfully built our image, the next step is for us to deploy it on our local Kubernetes cluster.

To do that, first, we need to write our manifest. A Kubernetes Manifest is essentially a configuration file usually written in YAML or JSON, that describes the resources we want in our cluster.

Here’s how we can proceed.

1. Creating a Manifest File

Similar to the Dockerfile, we'll create a file named deployment.yaml (any name can be used) in the root directory of our application. This file will house the instructions for Kubernetes on how to orchestrate our application.



    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sample-app
      labels:
        app: sample-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: sample-app
      template:
        metadata:
          labels:
            app: sample-app
        spec:
          containers:
          - name: sample-app
            image: test_image:latest
            imagePullPolicy: Never
            ports:
              - containerPort: 8082


Enter fullscreen mode Exit fullscreen mode

Here's the description of the Deployment section:

  • apiVersion and kind define the resource type and version.
  • metadata holds the name and labels of the deployment.
  • spec specifies the desired state of the deployment. We've set it to have 2 replicas and match labels with the selector.
  • template describes the pods to be created, and under containers, we provide the details of the container, including the image and port.

Pods are ephemeral, meaning they can be created, destroyed, and replaced as needed. The spec specification ensures that the desired number of replicas, as defined (2 in this instance), is maintained, and any deviations from this state are automatically rectified by the system i.e if a pod dies for any reason, another is created immediately. This level of dynamic management underscores the resilient and adaptable nature of Kubernetes.

2. Defining a Service
A Kubernetes Service is an abstraction that provides a consistent and reliable way to access applications running within a Kubernetes cluster, it essentially exposes a logical set of pods to external traffic. We'll write a service manifest that connects to our deployment.



    apiVersion: v1
    kind: Service
    metadata:
        name: sample-app-service
    spec: 
        selector: 
            app: sample-app
        ports:
            - protocol: TCP
              port: 80
              targetPort: 8082


Enter fullscreen mode Exit fullscreen mode

Here’s the complete manifest.



    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sample-app
      labels:
        app: sample-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: sample-app
      template:
        metadata:
          labels:
            app: sample-app
        spec:
          containers:
          - name: sample-app
            image: test_image:latest
            imagePullPolicy: Never
            ports:
              - containerPort: 8082
    ---
    apiVersion: v1
    kind: Service
    metadata:
        name: sample-app-service
    spec: 
        selector: 
            app: sample-app
        ports:
            - protocol: TCP
              port: 80
              targetPort: 8082


Enter fullscreen mode Exit fullscreen mode

Spinning up the Kubernetes Cluster

After writing our manifest, the next step is to spin up our Kubernetes cluster. This can be achieved by executing the command below in the terminal.



    minikube start


Enter fullscreen mode Exit fullscreen mode

This command may take some time to execute, depending on the computer and the internet speed.

Next, we apply the manifest using:



    kubectl apply -f <full path to manifest file>


Enter fullscreen mode Exit fullscreen mode

After applying our image, we confirm the pod status using:



    kubectl get pods 


Enter fullscreen mode Exit fullscreen mode

kubectl get pods command

This should show 2 replicas with a "Running" status, as illustrated in the provided screenshot.

Testing

Once we've verified that our pods are up and running, we can execute the service using the command:



    minikube service sample-app-service


Enter fullscreen mode Exit fullscreen mode

Result:

Successful result

Conclusion

In this comprehensive tutorial, we've delved deep into the crucial elements of Docker, containers, and Kubernetes. Starting from the basics, we've progressed to practical implementation by demystifying the creation of Dockerfiles and Kubernetes manifest files. Moving beyond theory, we've explored the setup of a local Kubernetes environment using Minikube and successfully deployed a containerized application in this environment.

Overall, this tutorial is an excellent starting point for anyone looking to get familiar with the intricacies of Docker and Kubernetes. It helps beginners build a solid foundation to confidently navigate these advanced technologies. From understanding the basics of containerization to grasping the role of Kubernetes in managing applications.

Resources

Here are some resources for further reading

Top comments (4)

Collapse
 
rathanak profile image
Rathanak

Can I have this kind of tutorial that work with
Complete laravel project (MySQL database ).

Collapse
 
anwareset profile image
Trianwar

I understand that these are meant to be on your local environment, nice explanation. Otherways, for production we can consider to use something like supervisord to run it instead of spin up local development server like php artisan serve

Collapse
 
thatcoolguy profile image
dami

You’re absolutely correct, since we’re running our application locally, setting up supervisord might be an overkill. Thank you for your feedback @anwareset

Collapse
 
yesin_arafatasif_8e6a3c8 profile image
Yesin Arafat Asif • Edited

Can you help me my application not run

My deployed file
It’s not working
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel
spec:
replicas: 2
selector:
matchLabels:
app: laravel
template:
metadata:
labels:
app: laravel
spec:
containers:
- name: laravel
image: yasinarafatasif/kubernet-demo:laravel-chat-app
env:
- name: DB_HOST
value: mysql
- name: DB_DATABASE
value: react-db
- name: DB_USERNAME
value: react-user
- name: DB_PASSWORD
value: Easin@bsl
ports:
- containerPort: 9000


apiVersion: v1
kind: Service
metadata:
name: laravel
labels:
app: laravel
spec:
type: NodePort
ports:
-port: 9000
targetPort: 9000
nodePort: 31000
selector:
app: laravel

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi


apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:latest
env:
- name: MYSQL_ROOT_PASSWORD
value: root
- name: MYSQL_DATABASE
value: react-db
- name: MYSQL_USER
value: react-user
- name: MYSQL_PASSWORD
value: Easin@bsl
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc


apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- protocol: TCP
port: 3306
targetPort: 3306

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: laravel
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx

rules:
-host: "laravel.local"
http:
paths:
-path: /

pathType: Prefix
backend:
service:
name: laravel

port:
number: 80