LEMP Stack on Kubernetes

LEMP Stack on Kubernetes

This setup is for practice only and suitable for production environments

The LEMP stack—Linux, Nginx (pronounced "Engine-X"), MySQL (or MariaDB), and PHP—has been a popular and powerful choice for building and deploying web applications due to its open-source nature, performance, and scalability. However, as applications become more complex and traffic increases, managing the infrastructure can be challenging. Kubernetes, the leading container orchestration platform, addresses this by allowing us to containerize the LEMP stack components and deploy them on Kubernetes, providing agility, scalability, and resilience. This setup streamlines deployments, automates management tasks, and ensures high availability for web applications. This article will guide you through deploying a LEMP stack on Kubernetes, exploring best practices and demonstrating how to use Kubernetes features to enhance the performance and reliability of your web applications.

The Components I used in Kubernetes are:

  1. Secrets - to store MYSQL related secrets like MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD and MYSQL_HOST

  2. Config maps - to store the php.ini file

  3. Deployment - the Hero of kubernetes (as i call it :-P ) to deploy our containers in pods

  4. Services - to expose our application for accessing

Here’s the code base:

secrets.yaml

Copy

apiVersion: v1
kind: Secret
metadata:
  name: mysql-root-pass
type: Opaque
stringData:
  password: R00t
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-user-pass
type: Opaque
stringData:
  username: username
  password: password
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-db-url
type: Opaque
stringData:
  database: database_db1
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-host
type: Opaque
stringData:
  host: mysql-service

Here, i have implemented all the secrets in a single file and i have used opaque type of secret and stringData. We can also use data block but using it expects us to keep the data in base64 encoded format. So, make sure you are opting stringData or data based on your requirement and preference.


configmap.yaml

Copy

apiVersion: v1
kind: ConfigMap
metadata:
  name: php-config
data:
  php.ini: |
    variables_order = "EGPCS"

variables_order = "EGPCS" sets the precedence order for how PHP handles incoming variables from different sources. It defines which source "wins" if there are name collisions (i.e., the same variable name is submitted via multiple methods).

In the EGPCS order:

  1. E (Environment): Variables set in the server's environment have the highest precedence.

  2. G (GET): Variables passed via the URL (query string) have the next highest precedence.

  3. P (POST): Variables submitted via the HTTP POST method.

  4. C (Cookie): Variables stored in cookies.

  5. S (Server): Server variables (like those in $_SERVER) have the lowest precedence.


deployment.yml

Copy

apiVersion: apps/v1
kind: Deployment
metadata:
  name: lemp-wp
  labels:
    app: lemp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lemp
  template:
    metadata:
      labels:
        app: lemp
    spec:
      containers:
      - name: nginx-php-container
        image: webdevops/php-nginx:alpine-3-php7
        ports:
        - containerPort: 80
        volumeMounts:
        - name: php-config-vol
          mountPath: /opt/docker/etc/php/php.ini
          subPath: php.ini
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-root-pass
              key: password
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: mysql-db-url
              key: database
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: mysql-user-pass
              key: username
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-user-pass
              key: password
        - name: MYSQL_HOST
          valueFrom:
            secretKeyRef:
              name: mysql-host
              key: host
      - name: mysql-container
        image: mysql:5.6
        ports:
        - containerPort: 3306
        env:
          - name: MYSQL_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql-root-pass
                key: password
          - name: MYSQL_DATABASE
            valueFrom:
              secretKeyRef:
                name: mysql-db-url
                key: database
          - name: MYSQL_USER
            valueFrom:
              secretKeyRef:
                name: mysql-user-pass
                key: username
          - name: MYSQL_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql-user-pass
                key: password
          - name: MYSQL_HOST
            valueFrom:
              secretKeyRef:
                name: mysql-host
                key: host
      volumes:
      - name: php-config-vol
        configMap:
          name: php-config

Services.yaml

Copy

apiVersion: v1
kind: Service
metadata:
  name: lemp-service
  labels:
    app: lemp
spec:
  type: NodePort
  selector:
    app: lemp
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30008
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: lemp
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306

The LEMP based application is exposed using nodePort mode and mysql-service is used a default clusterIP Service. A better approach would be using a stateful set for Database.