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:
- Basic understanding of PHP and the Laravel framework.
- Docker Desktop Installed. Here’s the link to download Docker Desktop.
- Minikube is required to create a local Kubernetes environment suitable for testing and development. Here’s the link to set it up.
- 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
After installation is complete, verify it by running:
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.
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,
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.
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.
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.
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
.
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.
EXPOSE 8082
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
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 .
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
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
Here's the description of the Deployment section:
-
apiVersion
andkind
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 undercontainers
, 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
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
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
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>
After applying our image, we confirm the pod status using:
kubectl get pods
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
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)
Can I have this kind of tutorial that work with
Complete laravel project (MySQL database ).
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
You’re absolutely correct, since we’re running our application locally, setting up
supervisord
might be an overkill. Thank you for your feedback @anwaresetCan 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