Orchestrating Microservices with Kubernetes: A Deep Dive into PHP 8/9, Laravel, and AWS ECS Integration
Leveraging Kubernetes for PHP Microservices: A Practical Approach
This post details the architectural considerations and practical implementation of orchestrating PHP 8/9 microservices, specifically within the Laravel framework, using Kubernetes. We’ll focus on a production-ready setup, integrating with AWS Elastic Container Service (ECS) for managed Kubernetes (EKS) and exploring essential components like service discovery, ingress, and persistent storage.
Containerizing Laravel Applications
The foundation of any microservices architecture on Kubernetes is a well-defined container image. For Laravel applications, this involves creating a Dockerfile that sets up the PHP environment, installs dependencies, and configures the web server.
Consider a typical Laravel application. We’ll need PHP, Composer, and a web server like Nginx or Apache. For this example, we’ll use Nginx with PHP-FPM.
Dockerfile Example
# Use an official PHP runtime as a parent image
FROM php:8.2-fpm
# Set the working directory in the container
WORKDIR /var/www/html
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libonig-dev \
libxml2-dev \
zip \
nginx \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install pdo pdo_mysql zip exif pcntl opcache
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
# Copy application code
COPY . /var/www/html
# Install Composer dependencies
RUN composer install --no-dev --optimize-autoloader
# Configure Nginx
COPY docker/nginx/default.conf /etc/nginx/sites-available/default
# Expose port 80
EXPOSE 80
# Start Nginx and PHP-FPM
CMD ["sh", "-c", "nginx -g 'daemon off;' && php-fpm"]
Nginx Configuration for PHP-FPM
server {
listen 80;
index index.php index.html index.htm;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/html/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000; # Assuming php-fpm service is named 'php-fpm'
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}
In this Nginx configuration, fastcgi_pass php-fpm:9000; assumes that PHP-FPM is running as a separate service within the Kubernetes cluster, often in a dedicated pod or as a sidecar. For simplicity in a single-container setup, you might directly use unix:/var/run/php/php8.2-fpm.sock if PHP-FPM is running within the same container.
Kubernetes Deployment and Service Definitions
Once containerized, we define Kubernetes resources to manage our Laravel microservices. This includes Deployments for managing application replicas and Services for network access and discovery.
Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-auth-service
labels:
app: laravel-auth
spec:
replicas: 3
selector:
matchLabels:
app: laravel-auth
template:
metadata:
labels:
app: laravel-auth
spec:
containers:
- name: app
image: your-docker-registry/laravel-auth-service:v1.0.0
ports:
- containerPort: 80
env:
- name: DB_HOST
value: "mysql-service" # Service name for the database
- name: CACHE_DRIVER
value: "redis"
- name: REDIS_HOST
value: "redis-service" # Service name for Redis
# Add other environment variables as needed (e.g., API keys, JWT secrets)
# Define resource requests and limits for better scheduling and stability
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Service Manifest
apiVersion: v1
kind: Service
metadata:
name: laravel-auth-service
spec:
selector:
app: laravel-auth
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP # Or LoadBalancer if exposing directly outside the cluster
The ClusterIP service type makes the service accessible only within the Kubernetes cluster. For external access, you’d typically use an Ingress controller or change the service type to LoadBalancer (which AWS EKS provisions as an ELB).
Integrating with AWS EKS
AWS EKS simplifies Kubernetes cluster management. When deploying to EKS, you’ll use kubectl configured to point to your EKS cluster. The process involves building your Docker image, pushing it to a registry (like Amazon ECR), and then applying your Kubernetes manifests.
Pushing to Amazon ECR
# Authenticate Docker to your ECR registry aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin YOUR_AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com # Tag your Docker image docker tag laravel-auth-service:latest YOUR_AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/laravel-auth-service:v1.0.0 # Push the image to ECR docker push YOUR_AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/laravel-auth-service:v1.0.0
Applying Kubernetes Manifests
# Ensure your kubectl is configured for your EKS cluster kubectl apply -f deployment.yaml kubectl apply -f service.yaml
Ingress for External Access and Routing
To expose your microservices to the internet and manage routing between them, an Ingress controller is essential. AWS EKS typically uses the AWS Load Balancer Controller, which provisions and manages AWS Application Load Balancers (ALBs).
Ingress Manifest Example
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservices-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
# For HTTPS, you'd add:
# alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
# alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:YOUR_AWS_ACCOUNT_ID:certificate/YOUR_CERT_ID
spec:
rules:
- host: api.yourdomain.com
http:
paths:
- path: /auth
pathType: Prefix
backend:
service:
name: laravel-auth-service
port:
number: 80
- path: /users
pathType: Prefix
backend:
service:
name: laravel-user-service # Assuming another microservice
port:
number: 80
# For HTTPS, you'd also specify a default backend or TLS configuration
# tls:
# - hosts:
# - api.yourdomain.com
# secretName: your-tls-secret # Kubernetes secret containing your TLS cert and key
This Ingress resource routes traffic based on the host and path. For example, requests to api.yourdomain.com/auth will be forwarded to the laravel-auth-service. The annotations are crucial for configuring the AWS ALB. Ensure your DNS records for api.yourdomain.com point to the DNS name of the provisioned ALB.
Persistent Storage with AWS EBS
For stateful applications or services that require persistent storage (e.g., file uploads, logs that need to survive pod restarts), Kubernetes Persistent Volumes (PVs) and Persistent Volume Claims (PVCs) are used. AWS EKS integrates with AWS Elastic Block Store (EBS) via the EBS CSI driver.
StorageClass for AWS EBS
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: aws-ebs-sc provisioner: ebs.csi.aws.com parameters: type: gp3 # Or gp2, io1, etc. fsType: ext4 reclaimPolicy: Retain # Or Delete volumeBindingMode: WaitForFirstConsumer
PersistentVolumeClaim Example
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: laravel-uploads-pvc
spec:
accessModes:
- ReadWriteOnce # For EBS, typically ReadWriteOnce
storageClassName: aws-ebs-sc
resources:
requests:
storage: 10Gi
This PVC can then be mounted into your Laravel application pods. For instance, to store user avatars or uploaded files:
Mounting PVC in Deployment
# ... inside the Deployment spec.template.spec.containers section ...
volumeMounts:
- name: uploads-volume
mountPath: "/var/www/html/storage/app/public" # Or your application's upload directory
volumes:
- name: uploads-volume
persistentVolumeClaim:
claimName: laravel-uploads-pvc
Remember to configure your Laravel application’s filesystem disk to use this mounted path.
Service Discovery and Communication Between Microservices
Kubernetes provides built-in DNS-based service discovery. When you define a Service with a specific name (e.g., mysql-service, redis-service), other pods within the same cluster can resolve and connect to it using that service name as a hostname. This abstracts away the underlying IP addresses and load balancing.
For inter-service communication, you can use the service names directly in your application’s configuration (as shown with DB_HOST and REDIS_HOST in the Deployment manifest). For more complex scenarios, consider using a service mesh like Istio or Linkerd, which can provide advanced features like mTLS, traffic splitting, and observability, though they add significant complexity.
Monitoring and Logging
Production-ready microservices require robust monitoring and logging. For Kubernetes, common solutions include:
- Metrics: Prometheus and Grafana are standard. Deploy Prometheus to scrape metrics from your pods (via annotations or service discovery) and Grafana to visualize them.
- Logging: A cluster-wide logging solution is crucial. Options include the EFK stack (Elasticsearch, Fluentd, Kibana) or Loki with Promtail and Grafana. Fluentd or Fluent Bit can be deployed as DaemonSets to collect logs from all nodes and forward them to a central store.
- Tracing: For distributed tracing, integrate libraries like Jaeger or Zipkin into your PHP applications and deploy their collectors within Kubernetes.
Ensure your Laravel applications are configured to output logs in a structured format (e.g., JSON) to facilitate parsing by log aggregation tools.
Conclusion
Orchestrating PHP microservices with Kubernetes on AWS EKS provides a scalable, resilient, and manageable platform. By carefully defining Docker images, Kubernetes manifests for Deployments and Services, and leveraging Ingress for routing, you can build sophisticated microservice architectures. Integrating with AWS services like ECR and EBS further enhances the production readiness of your deployments. Continuous monitoring and logging are paramount for maintaining the health and performance of your distributed system.