Photo by Marjan Blan | @marjanblan on Unsplash
Building a micro-services application with PHP, Kubernetes and Skaffold
6 min read
Hello World! Jump on board with me on this journey and we will learn how to setup and deploy a micro-services architecture using Kubernetes, Laravel Lumen and Skaffold.
1. Project Setup
First off, let’s create an empty directory with git initialised, this will be our project working folder, I am running on Ubuntu so I would run something similar to the following command:
mkdir ~/projects/php-and-k8s/ && git init ~/projects/php-and-k8s
Next up let’s create some kind of directory structure to hold our services and Kubernetes manifests.
Go ahead and create the following folder structure:
-- -- services/ -- manifests/
2. Creating a service
Create a Laravel Lumen application
Now lets create our shiny new micro-service inside the services folder, we will call this service hello-world, navigate to the services folder and run the following command:
composer create-project --prefer-dist laravel/lumen hello-world
This will create a new Laravel Lumen application inside the folder hello-world.
You will need composer installed on your machine, if you don’t have composer you can grab it here.
You will also need PHP installed, please refer to Laravel Lumen Server Requirements to install the necessary software.
3. Docker, Kubernetes and Skaffold
Dockerizing our Lumen Application
Kubernetes is a production-grade orchestration software that automates deployment, scaling and management of containerized applications and so, we will need to dockerize each of our micro-services to run on Kubernetes.
Create the following file at services/hello-world/Dockerfile with the contents below:
FROM php:8.0.0-apache # Update apt RUN apt-get update RUN apt-get install libcurl4 libcurl4-openssl-dev libzip-dev libpq-dev libpng-dev libfreetype6-dev libjpeg62-turbo-dev libxml2-dev -y # Install Composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Extensions RUN docker-php-ext-install pcntl opcache soap zip pdo_pgsql pdo_mysql RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install gd RUN pecl install -o -f redis && rm -rf /tmp/pear \ && docker-php-ext-enable redis COPY ./docker/php.ini /usr/local/etc/php/conf.d/ # Apache RUN a2enmod rewrite COPY ./docker/000-default.conf /etc/apache2/sites-available/000-default.conf COPY . /var/www/html # Remove dev composer packages RUN composer install --optimize-autoloader --no-dev RUN composer dump-autoload RUN chown -R www-data:www-data storage EXPOSE 80
The Dockerfile above will use the php:8.0.0-apache image, if you prefer to use a lower version of PHP simply change the version number to one of the supported tags that can be found on Docker.
You may have noticed a few required files in the Dockerfile above, these can be retrieved from a GitHub repository that I have created for this guide.
Download the files from mrbenosborne/php-microservice-docker and place them inside a new folder called services/hello-world/docker/, the files required are listed below:
It is good practice to add a .dockerignore file to our service so the build context is much smaller, copy the below to a new file at services/hello-world/.dockerignore
Writing our Kubernetes manifest
To deploy our application as a service on Kubernetes we will need to write a Kubernetes manifest file, we will create a simple Deployment and Service as described below.
Create the following file at services/hello-world/service.yaml this file will contain both our deployment and service spec.
apiVersion: apps/v1 kind: Deployment metadata: name: hello-world spec: replicas: 2 strategy: rollingUpdate: maxSurge: 2 maxUnavailable: 50% selector: matchLabels: app: hello-world-app template: metadata: labels: app: hello-world-app spec: containers: - name: hello-world image: hello-world imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: hello-world spec: type: NodePort ports: - name: http targetPort: 80 selector: app: hello-world-app
I won’t go into much detail regarding every part of the above 2 manifest specs as it is outside the remit of this guide but for my next guide I will show you how to add resource limits, horizontal pod scaling and more to Kubernetes Deployment specs.
The above is a very simple Deployment followed by a Service that forwards traffic onto port 80.
The image is called hello-world, you may be wondering why it is not a full name and version etc, this is because Skaffold will take care of injecting the image name for us when it builds each image, the name hello-world is just a reference.
Skaffold is a piece of software that handles building, pushing and deploying your application. Head over to https://skaffold.dev/ and download the binary.
Once installed you will need Kubectl too, head over to https://kubernetes.io/docs/tasks/tools/install-kubectl/ and following the instructions depending on your OS.
Right! Once that’s all done let’s create our Skaffold configuration file, create a new file in the root of our project at ./skaffold.yaml.
apiVersion: skaffold/v2beta7 kind: Config metadata: name: php-and-k8s build: artifacts: - image: hello-world context: services/hello-world deploy: kubectl: manifests: - services/hello-world/service.yaml
Great, now that’s done we are almost at the stage where we can run the cluster locally and hit our service.
Notice the image name above, hello-world this is the reference I referred to earlier in the Kubernetes deployment spec, if you change it in the deployment spec then you’ll also need to update it in skaffold.yaml.
We need one more thing to create a local cluster for testing purposes, head over to https://minikube.sigs.k8s.io/docs/start/ and install Minikube dependant on your OS.
4. Bringing up your Cluster
Sweet! You’ve have made it this far now for the big switch on ;)
Open up a terminal and start a local cluster with minikube, the following command can be used:
It may take a minute or two to bring up the cluster, you should see an output similar to the following:
minikube v1.13.1 on Ubuntu 20.04 ✨ Automatically selected the docker driver 👍 Starting control plane node minikube in cluster minikube 🔥 Creating docker container (CPUs=2, Memory=3900MB) ... 🐳 Preparing Kubernetes v1.19.2 on Docker 19.03.8 ... 🔎 Verifying Kubernetes components... 🌟 Enabled addons: default-storageclass, storage-provisioner 🏄 Done! kubectl is now configured to use "minikube" by default
Once Minikube has started you can now run the following command in a new terminal window, this will rebuild each service when your files change.
You should see output similar to the below:
Listing files to watch... - hello-world Generating tags... - hello-world -> hello-world:latest Some taggers failed. Rerun with -vdebug for errors. Checking cache... - hello-world: Not found. Building Found [minikube] context, using local docker daemon. Building [hello-world]...
Skaffold will now build each of our services, in our case just the one hello-world.
Once the above has finished you should see some output from Apache, run the following command to see our pods statuses.
kubectl get pods
You should see something similar to the below output:
NAME READY STATUS RESTARTS AGE hello-world-85fbbb7ffb-88wj5 1/1 Running 0 29s hello-world-85fbbb7ffb-wk6gw 1/1 Running 0 29s
As you can see we have 2 pods running our hello world service, this is because we specified replicas: 2 in our Deployment spec earlier on.
Sending a request to the service
Now our service is up and running we can send a request to the service by using the kubectl port forward command, this will forward all traffic from our local host to the service running in our cluster.
Run the following command in a new terminal window, replacing hello-world-85fbbb7ffb-88wj5 with whatever one of your pods name is.
kubectl port-forward hello-world-85fbbb7ffb-88wj5 8000:80
Now you can open http://localhost:8000/ in your browser and you should be presented with:
Lumen (8.2.1) (Laravel Components ^8.0)
You have now built and deployed successfully a Laravel Lumen application on Kubernetes using Skaffold and Minikube!
I hope you have enjoyed this guide, feel free to ask me any questions/feedback.
This guide only touches on the basic fundamentals but we can expand on this foundation and explore more into:
- Adding an Ingress Controller
- Adding a router such as Envoy or Traefik.
- Setting up a workflow for continuous integration/continuous delivery.
- You can find this complete project over at my GitHub, check out the repository github.com/mrbenosborne/php-laravel-k8s-ska...
Thanks for reading :)