Deploy Laravel Application บน Kubernetes พร้อม Redis Queue

Deploy Laravel Application บน Kubernetes พร้อม Redis Queue

การ Deploy Laravel ไปยัง Kubernetes เป็นแนวทางที่ยอดเยี่ยมสำหรับการสร้างระบบ Production-Ready ที่มีการจัดการ PHP-FPM, Nginx, MySQL, Redis และ Queue Workers อย่างครบถ้วน บทความนี้จะแนะนำวิธีการ Deploy Laravel บน Kubernetes Cluster พร้อมกับ Redis Queue สำหรับการประมวลผลงานแบบ Asynchronous

ทำไมต้อง Deploy Laravel บน Kubernetes

  • Microservices Architecture: แยก Laravel Application ออกเป็น Web Server, PHP-FPM, Worker และ Database Containers ที่เป็นอิสระ
  • Scalability: ปรับจำนวน Web Server และ Queue Workers ได้อย่างอิสระตามความต้องการของระบบ
  • Queue Jobs: Redis Queue สำหรับการประมวลผลงานที่ต้องเวลานาน (Long-Running Tasks)
  • Data Persistence: MySQL StatefulSet สำหรับการเก็บข้อมูล และ Redis สำหรับ Caching
  • High Availability: ระบบจะทำงานต่อได้หากมี Pod ตัวหนึ่งเกิดปัญหา

Architecture ของ Laravel บน Kubernetes

ระบบ Production-Ready ของ Laravel บน Kubernetes ประกอบด้วยส่วนประกอบหลัก ดังต่อไปนี้:

  • Nginx Service: ทำหน้าที่เป็น Reverse Proxy และ Load Balancer สำหรับจัดการคำขอจาก Clients
  • PHP-FPM Deployment: หลาย Replicas ที่รัน Laravel Application
  • Queue Workers: Separate Deployment สำหรับการประมวลผล Queue Jobs
  • MySQL StatefulSet: เก็บข้อมูลของ Application และรักษาความสอดคล้องของข้อมูล
  • Redis Deployment: สำหรับ Caching และ Queue Storage
  • ConfigMap และ Secret: เก็บ .env Configuration และ Sensitive Data อย่างปลอดภัย

1. สร้าง Namespace

kubectl create namespace laravel
kubectl config set-context --current --namespace=laravel

2. สร้าง Secret สำหรับ .env

เก็บ Laravel Configuration อย่างปลอดภัยโดยใช้ Kubernetes Secret:

cat > laravel.env << EOF
APP_KEY=base64:your-base64-encoded-key
APP_DEBUG=false
APP_URL=https://yourapp.com
DB_CONNECTION=mysql
DB_HOST=mysql.laravel.svc.cluster.local
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=your-secure-password
REDIS_HOST=redis.laravel.svc.cluster.local
REDIS_PASSWORD=your-redis-password
REDIS_PORT=6379
QUEUE_CONNECTION=redis
EOF

kubectl create secret generic laravel-env --from-file=laravel.env -n laravel

3. สร้าง MySQL StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: laravel
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "root-password"
        - name: MYSQL_DATABASE
          value: "laravel"
        - name: MYSQL_USER
          value: "laravel"
        - name: MYSQL_PASSWORD
          value: "your-secure-password"
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi

4. สร้าง MySQL Service

apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: laravel
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306

5. สร้าง Redis Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: laravel
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
        command:
          - redis-server
          - "--requirepass"
          - "your-redis-password"
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"

6. สร้าง Redis Service

apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: laravel
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379

7. สร้าง Laravel PHP-FPM Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-app
  namespace: laravel
spec:
  replicas: 3
  selector:
    matchLabels:
      app: laravel-app
  template:
    metadata:
      labels:
        app: laravel-app
    spec:
      containers:
      - name: php-fpm
        image: your-registry/laravel-app:latest
        ports:
        - containerPort: 9000
        env:
        - name: APP_ENV
          value: "production"
        volumeMounts:
        - name: laravel-env
          mountPath: /var/www/html/.env
          subPath: laravel.env
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"
        livenessProbe:
          tcpSocket:
            port: 9000
          initialDelaySeconds: 10
          periodSeconds: 10
      volumes:
      - name: laravel-env
        secret:
          secretName: laravel-env

8. สร้าง Nginx Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: laravel
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: default.conf
        - name: laravel-app
          mountPath: /var/www/html
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-config
      - name: laravel-app
        emptyDir: {}

9. สร้าง Nginx ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: laravel
data:
  default.conf: |
    upstream php-fpm {
      server laravel-app:9000;
    }
    server {
      listen 80;
      server_name _;
      root /var/www/html/public;
      index index.php;
      location ~ \.php$ {
        fastcgi_pass php-fpm;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
      location / {
        try_files $uri $uri/ /index.php?$query_string;
      }
    }

10. สร้าง Queue Workers Deployment

สร้าง Separate Deployment สำหรับการประมวลผล Queue Jobs จาก Redis Queue:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-worker
  namespace: laravel
spec:
  replicas: 2
  selector:
    matchLabels:
      app: laravel-worker
  template:
    metadata:
      labels:
        app: laravel-worker
    spec:
      containers:
      - name: queue-worker
        image: your-registry/laravel-app:latest
        command: ["php", "artisan", "queue:work", "redis", "--tries=3"]
        env:
        - name: APP_ENV
          value: "production"
        volumeMounts:
        - name: laravel-env
          mountPath: /var/www/html/.env
          subPath: laravel.env
        resources:
          limits:
            memory: "256Mi"
            cpu: "250m"
          requests:
            memory: "128Mi"
            cpu: "100m"
      volumes:
      - name: laravel-env
        secret:
          secretName: laravel-env

11. สร้าง Nginx Service

apiVersion: v1
kind: Service
metadata:
  name: laravel-app
  namespace: laravel
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

ตรวจสอบและจัดการ Laravel บน Kubernetes

# สร้าง Database
kubectl exec -it mysql-0 -n laravel -- \
  mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS laravel;"

# รันการ Migrate ของ Laravel
kubectl exec -it $(kubectl get pod -l app=laravel-app -n laravel -o jsonpath='{.items[0].metadata.name}') -n laravel -- \
  php artisan migrate --force

# ตรวจสอบสถานะของ Pods
kubectl get pods -n laravel

# ตรวจสอบสถานะของ Services
kubectl get svc -n laravel

HorizontalPodAutoscaler สำหรับ Auto Scaling

สามารถตั้งค่า HPA เพื่อให้ระบบสามารถ Scale ได้อัตโนมัติตามการใช้งาน CPU:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: laravel-app-hpa
  namespace: laravel
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: laravel-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Best Practices สำหรับ Laravel บน Kubernetes

  • ใช้ Secret สำหรับเก็บข้อมูลที่มีความเสี่ยง แทนการใช้ Environment Variables โดยตรง
  • กำหนด Resource Requests และ Limits เพื่อควบคุมการใช้งานทรัพยากร
  • เพิ่มจำนวน Queue Workers ตามความต้องการของงาน Asynchronous
  • ใช้ Health Checks (Liveness Probe และ Readiness Probe) เพื่อตรวจสอบสถานะของ Pods
  • มีกลยุทธ์ Database Migration ที่ปลอดภัย เพื่อหลีกเลี่ยงข้อมูลสูญหาย
  • ใช้ PersistentVolume สำหรับเก็บข้อมูลที่ต้องการเก็บรักษาอย่างยาวนาน
  • ทำการ Monitor และ Logging ด้วยเครื่องมือเช่น Prometheus, ELK Stack หรือ CloudWatch

บทสรุป

การ Deploy Laravel บน Kubernetes ให้ความยืดหยุ่นและความเสถียรในการจัดการแอปพลิเคชัน ด้วยการ Scale อัตโนมัติ, Caching ด้วย Redis และการประมวลผล Queue Jobs อย่างมีประสิทธิภาพ สามารถสร้างระบบที่พร้อมสำหรับ Production และรองรับภาระงานที่สูงได้