Deploying a Containerized Laravel Application to a Local Kubernetes Cluster: A Step-by-Step Guide
Tags: kubernetes, docker, laravel, beginners
Published: 2023-08-19 21:26:20


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](https://www.docker.com/products/docker-desktop/).
3. Minikube is required to create a local Kubernetes environment suitable for testing and development. Here’s the [link](https://minikube.sigs.k8s.io/docs/start/) 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](https://kubernetes.io/docs/tasks/tools/) 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:

```php
    laravel new sample-app
```
After installation is complete, verify it by running:

```php
    php artisan serve
```

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.

```php
    FROM php:7.4-fpm as php
```

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, 

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

3\. **Clearing Local Repository of Retrieved Package Files**

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

```php
    RUN apt-get clean
```

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.

```php
    RUN mkdir /app
    ADD . /app
    WORKDIR /app
```

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.

```php
    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 
```

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`.

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

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.

 ```php 
    EXPOSE 8082
```
Here’s the complete Dockerfile.

```php
    # 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
    
```    


## 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.

```php
    docker build -t test-image .
```

> 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:

```php
    minikube image load test-image
```

## 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.

```php
    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
   ``` 

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.

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

Here’s the complete manifest.

```php
    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
```

## 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.

```php
    minikube start
```

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

Next, we apply the manifest using:

```php
    kubectl apply -f <full path to manifest file>
```

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

```php
    kubectl get pods 
```


![kubectl get pods command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/susgoakvwh6h9p2f3b6c.png)


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:

```php
    minikube service sample-app-service
```

Result:

![Successful result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdny3lhbeykws4260m25.png)


## 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


- [Deploy a Docker Container to a Kubernetes Cluster using Kubernetes CLI](https://sweetcode.io/how-to-deploy-docker-containers-to-the-kubernetes-cluster-using-kubernetes-cli/)
- [Setup Local Kubernetes Cluster With Docker Desktop](https://dev.to/rizways/setup-local-kubernetes-cluster-with-docker-desktop-1e7k)