DEV Community

Cover image for Harnessing Kubernetes for Hosting MySQL and Laravel Applications
Enwemasor Barnabas
Enwemasor Barnabas

Posted on

Harnessing Kubernetes for Hosting MySQL and Laravel Applications

Kubernetes has become the infrastructure for scalability; high performance and high availability, development teams are integrating containerization into their development processes; because it reduces the release cycle time; deployment processes are faster with it and development is easier,

In this brief exercise, we'll deploy a Laravel application and establish connectivity between the Laravel instance and a MySQL database running within a Kubernetes cluster.

Prerequisite

Setup a Laravel project by either

With the Kubernetes cluster ready and the Laravel project configured, it's time to configure our Kubernetes resources to host our application.

Create Kubernetes Secret

We are going to utilize Kubernetes Secret to store environment variables needed by both Mysql container and Laravel containers; on the root of your Laravel project create a file deployments/envs.yaml and paste

apiVersion: v1
kind: Secret
metadata:
  name: stng-secrets
stringData:
  APP_NAME: "App Name"
  APP_ENV: "local"
  APP_KEY: "base64:rvpuu7UF/tQqbfDRy6xIPDNwM/I5O00JQ+dohwZi8So="
  APP_DEBUG: "true"
  APP_URL: "http://127.0.0.1:8000"
  DB_CONNECTION: "mysql"
  DB_HOST: "stickersng-mysql" /**Must matched the MySQL service name**/
  DB_PORT: "3306"
  DB_DATABASE: "stickersng-db"
  DB_USERNAME: "stickersng"
  DB_PASSWORD: "stickersng"

Enter fullscreen mode Exit fullscreen mode

The above yaml file utilizes stringData to create secrets which will be accessed by the MySQL and the Laravel containers as environment variables;

Create Mysql Pods and Service

In other to get our MySQL server up and running we need to:

Create a service resource

This will be the Laravel application entry point when connecting to the database

PersistentVolumeClaim

The PersistentVolumeClaim resource will request for a persistent volume which will enable data stored in the Mysql database to live beyond the MySQL pod live cycle

Deployment

The Deployment will use the mysql:8.0 docker image to create a container, mount the persistent volume to the container, and specify how the above service can resolve its containers.

In root/deployments/ of your Laravel project create a file mysql-deployment.yaml and paste

apiVersion: v1
kind: Service
metadata:
  name: stickersng-mysql
  labels:
    app: stickersng
spec:
  ports:
  - port: 3306
  selector:
    app: stickersng
    tier: mysql
  clusterIP: None
---
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the service
  • spec.ports: Declares TCP port to be 3306; this means that all requests to all containers that match the service selector will be to port 3306
  • spec.selector: Declares the label/selector identifying attributes; this is how the service matches pod to forward traffic
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: stickersng-mysql-pv-claim
  labels:
    app: stickersng
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the PersistentVolumeClaim which will be used to create a volume mount on the Mysql pod
  • spec.resources.request.storage: Specifies the persistent volume capacity
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stickersng-mysql
  labels:
    app: stickersng
spec:
  selector:
    matchLabels:
      app: stickersng
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: stickersng
        tier: mysql
    spec:
      containers:
      - image: mysql:8.0
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: stng-secrets
              key: DB_PASSWORD
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: stng-secrets
              key: DB_DATABASE
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: stng-secrets
              key: DB_USERNAME
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: stng-secrets
              key: DB_PASSWORD
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: stickersng-mysql-pv-claim
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the deployment
  • spec.selector.matchLabels: Specifies the selector matching labels for the deployment
  • spec.template.spec.containers.image: Declares the image for running the Mysql container
  • spec.template.spec.containers.env: Declares environment variables using values from the Secret we created previously; the values for spec.template.spec.containers.env.valueFrom.key must match a key in the Secret yaml file (if any set key is not resolved correctly the container pods will not be ready)
  • spec.template.spec.volumes.persistentVolumeClaim: Declares which persistent volume to be used
  • spec.template.spec.containers.volumeMounts: Declares the path to mount the volume on the containers.

Combine the code snippet above into the mysql-deployment.yaml file created earlier.
The above yaml file will create 3 resources. A Secret,Deployment and a PersistentVolumeCliam

Package the Laravel project into an image

Kubernetes serves as a container orchestration system, with containers executing images. To utilize Kubernetes for hosting our Laravel application, which currently isn't in image form, we need to build the project into an image. To accomplish this, create a Dockerfile at the root of your project with the following content.: You will need docker installed and configured with dockerhub

# Use the official PHP image as the base
FROM php:8.2-fpm

# Set working directory
WORKDIR /var/www/html

# Install dependencies
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libzip-dev \
    zip \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd pdo pdo_mysql zip

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Copy composer files and install dependencies
COPY composer.json ./
RUN composer install --no-scripts --no-autoloader

# Copy the rest of the application code
COPY . .

# Set permissions
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
RUN chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache

# Generate autoload files
RUN composer dump-autoload

# Expose port 9000 and start php-fpm server
EXPOSE 8000
CMD php artisan serve --host=0.0.0.0 --port=8000

Enter fullscreen mode Exit fullscreen mode

This Dockerfile will use the official php8.2 image as the base image subsequent lines in the Dockerfile install and copy all necessary files and packages required in other to run the Laravel application in isolation. Run the below command on your terminal at the root of your project to build the image.

docker build -t [YOUR_DOCKER_HUB_USER_ID]/[IMAGE_NAME]:[IMAGE_TAG] .
Enter fullscreen mode Exit fullscreen mode

This process is a long one; a new image will be built and available on your host machine; for Kubernetes to utilize the image it needs to be available in a global container registry like Dockerhub or any other container registry provided by cloud providers

docker push [YOUR_DOCKER_HUB_USER_ID]/[IMAGE_NAME]:[IMAGE_TAG]
Enter fullscreen mode Exit fullscreen mode

Create the Laravel App Deployment

Now that we have our image ready and available over a container registry, we can create the Kubernetes deployment that will host the application. In root/deployments/ of your Laravel project create a file api-deployment.yaml and paste

apiVersion: v1
kind: Service
metadata:
  name: stickersng-api
  labels:
    app: stickersng-api
spec:
  ports:
  - port: 8000
  selector:
    app: stickersng-api
    tier: backend-api
  type: LoadBalancer
---
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the service
  • spec.ports: Declares TCP port to be 8000; this means that all requests to all containers that match the service selector will be to port 8000
  • spec.selector: Declares the label/selector identifying attributes; this is how the service matches pod to forward traffic
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: stickersng-api
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the PersistentVolumeClaim which will be used to create a volume mount.
  • spec.resources.request.storage: Specifies the persistent volume capacity
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stickersng-api
  labels:
    app: stickersng-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: stickersng-api
      tier: backend-api
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: stickersng-api
        tier: backend-api
    spec:
      initContainers:
      - name: setup-storage
        image: busybox
        command: ["sh", "-c", "mkdir -p /var/www/html/storage/framework/views/ && mkdir -p /var/www/html/storage/framework/cache/ && mkdir -p /var/www/html/storage/framework/sessions/ && chown -R www-data:www-data /var/www/html/storage/framework/sessions/"]
        volumeMounts:
        - name: stickersng-api-persistent-storage
          mountPath: /var/www/html/storage
      containers:
      - image: [YOUR_DOCKER_HUB_USER_ID]/[IMAGE_NAME]:[IMAGE_TAG]
        name: stickersng-api
        envFrom:
        - secretRef:
            name: stng-secrets
        ports:
        - containerPort: 8000
          name: stickersng-api
        volumeMounts:
        - name: stickersng-api-persistent-storage
          mountPath: /var/www/html/storage
      volumes:
      - name: stickersng-api-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
Enter fullscreen mode Exit fullscreen mode
  • metadata.name: Declares the name of the deployment
  • spec.selector.matchLabels: Specifies the selector matching labels for the deployment
  • spec.template.spec.containers.image: Declares the image for running the Laravel application.
  • spec.template.spec.containers.envFrom: Declares environment variables using values from the Secret we created previously;
  • spec.template.spec.volumes.persistentVolumeClaim: Declares which persistent volume to the use
  • spec.template.spec.containers.volumeMounts: Declares the path to mount the volume on the containers.
  • spec.template.spec.initContainers: is a temporary container that runs commands that create files required by Laravel to run on the mounted volumes, before starting the Laravel container.

Combine the code snippet above into the api-deployment.yaml file created earlier.

Now that we have created the yaml files for all the resources we need to host our application, we need a Kustomization yaml to group all these resource configuration files. This enables us to initiate all resources with a single command and delete them with a single command as well.

In root/deployments/ of your Laravel project create a file kustomization.yaml and paste

resources:
- envs.yaml
- mysql-deployment.yaml
- api-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Start up your Infrastructure

At this point, the deployments folder at the root of your Laravel application should contain the following files:

  • envs.yaml
  • mysql-deployment.yaml
  • api-deployment.yaml
  • kustomization.yaml

On your terminal cd into the deployment folder and run the following command

kubectl apply -k ./ 
Enter fullscreen mode Exit fullscreen mode

This command uses the kubectl command line tool to apply the resources specified in the Kustomization yaml file to your Kubernetes cluster;

To monitor your resources you can use the commands below to check the resource's status as they are being created

kubectl get services // get the list service
Enter fullscreen mode Exit fullscreen mode
kubectl get secrets // get the list of secrets
Enter fullscreen mode Exit fullscreen mode
kubectl get pvc // get the list of persistent volume claims
Enter fullscreen mode Exit fullscreen mode
kubectl get pods // get the list of pods
Enter fullscreen mode Exit fullscreen mode
minikube service stickersng-api --url // if you are using minikube for your cluster; this will return your Laravel URL
Enter fullscreen mode Exit fullscreen mode

With your application now running in Kubernetes pods, you will be able to visit the Laravel app service IP and you will see a preview of the application welcome page.

Kubernetes offers a robust infrastructure for deploying and managing containerized applications like MySQL and Laravel.

To delete the resources created so far, on your terminal cd into the deployment folder and run the following command

kubectl delete -k ./ 
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, Kubernetes offers a robust infrastructure for deploying and managing containerized applications like MySQL and Laravel. By following the steps outlined in this exercise, you've learned how to leverage Kubernetes concepts such as deployments, services, volumes, and secrets to set up a scalable and resilient environment for hosting your Laravel application alongside a MySQL database.

Through Kubernetes, you can achieve scalability, high performance, and high availability, crucial factors for modern development workflows. By containerizing your applications and utilizing Kubernetes for orchestration, you streamline deployment processes, reduce release cycle times, and make development more manageable.

Remember, this exercise is just a starting point. Kubernetes offers a vast array of features and configurations that you can explore to tailor your deployment setup to your specific needs. Keep experimenting, learning, and optimizing your Kubernetes infrastructure to ensure your applications run smoothly and efficiently in any environment.

Top comments (0)