• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 9+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Dockerizing and Orchestrating Legacy PHP Systems on Modern Google Cloud Infrastructure

Dockerizing and Orchestrating Legacy PHP Systems on Modern Google Cloud Infrastructure

Containerizing the Legacy PHP Application

The first critical step is to encapsulate the existing PHP application within a Docker container. This involves creating a Dockerfile that defines the environment, dependencies, and startup commands for your application. For a typical legacy PHP application, this often means dealing with older PHP versions, specific extensions, and potentially a monolithic codebase.

Let’s assume a common scenario: a PHP application running on Apache with MySQL. We’ll aim for a multi-stage build to keep the final image lean.

Dockerfile for PHP Application

# Stage 1: Build dependencies (if any complex compilation is needed)
# This stage is often omitted for simpler PHP apps but useful for extensions
# FROM php:8.1-fpm as builder
# RUN docker-php-ext-install pdo_mysql && docker-php-ext-enable pdo_mysql

# Stage 2: Production image
FROM php:8.1-apache

# Install necessary PHP extensions. Adjust as per your application's needs.
# Common ones include: mysqli, pdo_mysql, gd, intl, zip, mbstring
RUN apt-get update && apt-get install -y \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libicu-dev \
    unzip \
    git \
    vim \
    cron \
    && rm -rf /var/lib/apt/lists/* \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd pdo_mysql zip intl mbstring opcache \
    && a2enmod rewrite

# Copy application code
COPY . /var/www/html/

# Set working directory
WORKDIR /var/www/html/

# Install Composer dependencies (if applicable)
# Ensure composer.json and composer.lock are in the root of your COPY source
COPY composer.json composer.lock /var/www/html/
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Clean up composer cache
RUN rm -rf /root/.composer/cache

# Configure Apache for PHP
COPY apache/000-default.conf /etc/apache2/sites-available/000-default.conf

# Expose port 80
EXPOSE 80

# Set permissions (adjust if your application needs specific user/group)
RUN chown -R www-data:www-data /var/www/html/ && chmod -R 755 /var/www/html/

# Enable Apache modules
RUN a2enmod headers expires ssl

# Enable opcache
RUN docker-php-ext-enable opcache

# Enable cron jobs if any
# COPY cron/crontab /etc/cron.d/my-cron
# RUN chmod 0644 /etc/cron.d/my-cron && crontab /etc/cron.d/my-cron && touch /var/log/cron.log

# Start Apache in foreground
CMD ["apache2-foreground"]

Apache Configuration Example

Create a directory named apache in the same directory as your Dockerfile and place a configuration file, e.g., 000-default.conf, inside it.

<VirtualHost *:80>
    DocumentRoot /var/www/html/
    <Directory /var/www/html/>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

After creating the Dockerfile and any necessary supporting files (like the Apache config), build the Docker image:

docker build -t your-dockerhub-username/legacy-php-app:v1.0 .

Push this image to a container registry that Google Cloud can access, such as Google Container Registry (GCR) or Artifact Registry.

docker push your-dockerhub-username/legacy-php-app:v1.0

Orchestrating with Google Kubernetes Engine (GKE)

Google Kubernetes Engine (GKE) is the ideal platform for orchestrating containerized legacy applications. It provides robust features for deployment, scaling, and management.

Kubernetes Deployment Manifest

We’ll define a Kubernetes Deployment to manage our PHP application pods and a Service to expose it.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy-php-app-deployment
  labels:
    app: legacy-php-app
spec:
  replicas: 3 # Start with 3 replicas for HA
  selector:
    matchLabels:
      app: legacy-php-app
  template:
    metadata:
      labels:
        app: legacy-php-app
    spec:
      containers:
      - name: legacy-php-app
        image: gcr.io/your-gcp-project-id/legacy-php-app:v1.0 # Replace with your GCR/Artifact Registry path
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /healthz.php # Assuming you have a health check endpoint
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 20
        readinessProbe:
          httpGet:
            path: /healthz.php
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
        env: # Example environment variables
        - name: DB_HOST
          value: "mysql-service" # This will be the Kubernetes Service name for your MySQL DB
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: mysql-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-credentials
              key: password
        - name: DB_NAME
          value: "legacy_db"
      # If your app needs persistent storage for uploads/logs, configure volumes here
      # volumes:
      # - name: app-storage
      #   persistentVolumeClaim:
      #     claimName: legacy-php-app-pvc
      # containers:
      # - name: legacy-php-app
      #   ...
      #   volumeMounts:
      #   - name: app-storage
      #     mountPath: /var/www/html/uploads # Example mount path
---
apiVersion: v1
kind: Service
metadata:
  name: legacy-php-app-service
spec:
  selector:
    app: legacy-php-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP # Use LoadBalancer for external access, or Ingress

Database Considerations

For the database, you have several options:

  • Managed Cloud SQL Instance: This is the recommended approach for production. Provision a Cloud SQL instance (MySQL, PostgreSQL, etc.) and configure your GKE application to connect to it. You’ll need to manage network connectivity (e.g., using Private IP or Cloud SQL Auth Proxy).
  • Database within Kubernetes: Deploy MySQL or another database directly within your GKE cluster using a StatefulSet. This is simpler for development/testing but requires more operational overhead for production (backups, HA, patching).

If using Cloud SQL, you’ll typically use the Cloud SQL Auth Proxy sidecar container or configure Private IP for direct access. For secrets like database credentials, use Kubernetes Secrets, ideally populated from Google Secret Manager.

Ingress for External Access

To expose your application to the internet, you’ll use an Ingress controller. Google Cloud’s Managed Instance Group (MIG) based Ingress is a common choice.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: legacy-php-app-ingress
  annotations:
    kubernetes.io/ingress.class: "gce" # For Google Cloud's native Ingress
    # If using cert-manager for SSL:
    # cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: legacy-php-app-service
            port:
              number: 80
  # If using TLS/SSL:
  # tls:
  # - hosts:
  #   - your-domain.com
  #   secretName: legacy-php-app-tls-secret # Kubernetes secret containing your TLS cert and key

Apply these manifests to your GKE cluster:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml

Monitoring and Logging

Robust monitoring and logging are crucial for any production system, especially when migrating legacy applications. GKE integrates well with Google Cloud’s operations suite (formerly Stackdriver).

Enabling Cloud Operations for GKE

Ensure that the Cloud Operations for GKE add-on is enabled for your GKE cluster. This automatically collects logs and metrics from your pods and nodes.

You can verify this during cluster creation or by updating an existing cluster:

gcloud container clusters update YOUR_CLUSTER_NAME \
    --update-addons=CloudLogging=ENABLED,CloudMonitoring=ENABLED \
    --zone=YOUR_CLUSTER_ZONE # or --region=YOUR_CLUSTER_REGION

Custom Metrics and Log Analysis

For application-specific metrics, consider using Prometheus and exporting them to Cloud Monitoring, or directly instrumenting your PHP application to send metrics to Cloud Monitoring’s custom metrics API. For logs, ensure your PHP application writes to stdout and stderr, which GKE will capture. You can then use Cloud Logging’s powerful query language to analyze these logs.

Example of writing logs to stdout/stderr in PHP:

<?php
error_log("This is an informational message.");
file_put_contents('php://stderr', "This is an error message.\n");
?>

CI/CD Pipeline Integration

Automating the build, test, and deployment process is key to efficient operations. Cloud Build is Google Cloud’s CI/CD platform that integrates seamlessly with GKE and GCR/Artifact Registry.

Cloud Build Configuration

Create a cloudbuild.yaml file in your repository’s root:

steps:
# Build the Docker image
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/legacy-php-app:$COMMIT_SHA', '.']
  id: 'Build Docker Image'

# Push the Docker image to GCR/Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/legacy-php-app:$COMMIT_SHA']
  id: 'Push Docker Image'

# Deploy to GKE
- name: 'gcr.io/cloud-builders/kubectl'
  args:
  - 'apply'
  - '-f'
  - 'kubernetes/deployment.yaml' # Path to your deployment manifest
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=YOUR_CLUSTER_ZONE' # Or CLOUDSDK_COMPUTE_REGION
  - 'CLOUDSDK_CONTAINER_CLUSTER=YOUR_CLUSTER_NAME'
  id: 'Deploy to GKE'
  waitFor: ['Push Docker Image'] # Ensure image is pushed before deploying

# Optional: Update deployment with new image tag
- name: 'gcr.io/cloud-builders/kubectl'
  args:
  - 'set'
  - 'image'
  - 'deployment/legacy-php-app-deployment'
  - 'legacy-php-app=gcr.io/$PROJECT_ID/legacy-php-app:$COMMIT_SHA'
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=YOUR_CLUSTER_ZONE' # Or CLOUDSDK_COMPUTE_REGION
  - 'CLOUDSDK_CONTAINER_CLUSTER=YOUR_CLUSTER_NAME'
  id: 'Update Deployment Image'
  waitFor: ['Deploy to GKE']

images:
- 'gcr.io/$PROJECT_ID/legacy-php-app:$COMMIT_SHA'

options:
  logging: CLOUD_LOGGING_ONLY
  # If using private GKE clusters, you might need to configure network access for Cloud Build
  # machineType: 'N1_HIGHCPU_8' # Example for larger builds

Configure a trigger in Cloud Build to watch your repository (e.g., on push to the `main` branch) and execute this build configuration. Ensure the Cloud Build service account has the necessary permissions to push to GCR/Artifact Registry and deploy to GKE.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Step-by-Step: Diagnosing indexing lock conflicts and high CPU during bulk stock updates on DigitalOcean Servers
  • How to Debug and Fix memory leaks and socket exhaustion in daemon processes in Modern C++ Applications
  • Infrastructure as Code: Provisioning Secure PHP Clusters on DigitalOcean Using Terraform
  • Fixing Slow Largest Contentful Paint (LCP) caused by unoptimized database queries in Legacy Laravel Codebases Without Breaking API Contracts
  • An Auditor’s Checklist for Securing Laravel Backends on Google Cloud

Copyright © 2026 · Vinay Vengala