ai

WordPress Headless กับ Pod Scheduling — วิธีใช้

WordPress Headless กับ Pod Scheduling — วิธีใช้

WordPress Headless คืออะไร

WordPress Headless กับ Pod Scheduling — วิธีใช้

WordPress Headless คือการใช้ WordPress เป็น Content Management System (CMS) อย่างเดียว โดยแยก Frontend ออกมาใช้ Framework สมัยใหม่ เช่น Next.js, Nuxt.js หรือ Astro WordPress ทำหน้าที่เป็น API Server ส่งข้อมูลผ่าน REST API หรือ GraphQL

เนื้อหาเกี่ยวข้อง — ทำความเข้าใจ financial freedom pyramid

ข้อดีคือ Content Editor ยังใช้ WordPress ที่คุ้นเคย ส่วน Frontend ได้ Performance ที่ดีกว่า รองรับ Static Generation (SSG), Server-side Rendering (SSR) และ Edge Rendering เมื่อรันบน Kubernetes จะได้ Scalability และ High Availability เพิ่มเติม

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง Data Lakehouse High Availability HA Setup

Kubernetes Deployment สำหรับ WordPress Headless

# === WordPress Headless on Kubernetes ===
# wordpress-deployment.yaml

apiVersion: v1
kind: Namespace
metadata:
 name: wordpress
---
# === MySQL StatefulSet ===
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: mysql
 namespace: wordpress
spec:
 serviceName: mysql
 replicas: 1
 selector:
 matchLabels:
 app: mysql
 template:
 metadata:
 labels:
 app: mysql
 spec:
 # Pod Scheduling — ใช้ Node ที่มี SSD
 nodeSelector:
 disk-type: ssd
 containers:
 - name: mysql
 image: mysql:8.0
 ports:
 - containerPort: 3306
 env:
 - name: MYSQL_ROOT_PASSWORD
 valueFrom:
 secretKeyRef:
 name: mysql-secret
 key: root-password
 - name: MYSQL_DATABASE
 value: wordpress
 resources:
 requests:
 cpu: "500m"
 memory: 1Gi
 limits:
 cpu: "2"
 memory: 4Gi
 volumeMounts:
 - name: mysql-data
 mountPath: /var/lib/mysql
 livenessProbe:
 exec:
 command: ["mysqladmin", "ping", "-h", "localhost"]
 initialDelaySeconds: 30
 periodSeconds: 10
 readinessProbe:
 exec:
 command: ["mysql", "-h", "localhost", "-u", "root",
 "-p$(MYSQL_ROOT_PASSWORD)", "-e", "SELECT 1"]
 initialDelaySeconds: 10
 periodSeconds: 5
 volumeClaimTemplates:
 - metadata:
 name: mysql-data
 spec:
 accessModes: ["ReadWriteOnce"]
 storageClassName: gp3-ssd
 resources:
 requests:
 storage: 50Gi
---
# === WordPress Deployment ===
apiVersion: apps/v1
kind: Deployment
metadata:
 name: wordpress
 namespace: wordpress
spec:
 replicas: 3
 selector:
 matchLabels:
 app: wordpress
 template:
 metadata:
 labels:
 app: wordpress
 spec:
 # Pod Anti-affinity — กระจาย Pods ไปคนละ Node
 affinity:
 podAntiAffinity:
 preferredDuringSchedulingIgnoredDuringExecution:
 - weight: 100
 podAffinityTerm:
 labelSelector:
 matchExpressions:
 - key: app
 operator: In
 values: ["wordpress"]
 topologyKey: kubernetes.io/hostname
 # Topology Spread — กระจายข้าม AZ
 topologySpreadConstraints:
 - maxSkew: 1
 topologyKey: topology.kubernetes.io/zone
 whenUnsatisfiable: DoNotSchedule
 labelSelector:
 matchLabels:
 app: wordpress
 containers:
 - name: wordpress
 image: wordpress:6.5-php8.3-fpm
 ports:
 - containerPort: 9000
 env:
 - name: WORDPRESS_DB_HOST
 value: mysql.wordpress.svc.cluster.local
 - name: WORDPRESS_DB_NAME
 value: wordpress
 - name: WORDPRESS_DB_USER
 value: root
 - name: WORDPRESS_DB_PASSWORD
 valueFrom:
 secretKeyRef:
 name: mysql-secret
 key: root-password
 - name: WORDPRESS_CONFIG_EXTRA
 value: |
 define('WP_HOME', 'https://api.example.com');
 define('WP_SITEURL', 'https://api.example.com');
 define('HEADLESS_MODE_CLIENT_URL', 'https://www.example.com');
 define('GRAPHQL_DEBUG', false);
 resources:
 requests:
 cpu: "250m"
 memory: 256Mi
 limits:
 cpu: "1"
 memory: 1Gi
 volumeMounts:
 - name: wp-content
 mountPath: /var/www/html/wp-content
 readinessProbe:
 tcpSocket:
 port: 9000
 initialDelaySeconds: 15
 periodSeconds: 5
 livenessProbe:
 tcpSocket:
 port: 9000
 initialDelaySeconds: 30
 periodSeconds: 10
 volumes:
 - name: wp-content
 persistentVolumeClaim:
 claimName: wp-content-pvc
---
# === Nginx Sidecar (หรือแยก Deployment) ===
apiVersion: v1
kind: ConfigMap
metadata:
 name: nginx-config
 namespace: wordpress
data:
 default.conf: |
 server {
 listen 80;
 server_name _;
 root /var/www/html;
 index index.php;

 # CORS Headers สำหรับ Headless
 add_header Access-Control-Allow-Origin "https://www.example.com";
 add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
 add_header Access-Control-Allow-Headers "Authorization, Content-Type";

 # REST API
 location /wp-json/ {
 try_files $uri $uri/ /index.php?$args;
 }

 # GraphQL
 location /graphql {
 try_files $uri $uri/ /index.php?$args;
 }

 # Block wp-admin for non-authenticated
 location /wp-admin {
 allow 10.0.0.0/8;
 deny all;
 try_files $uri $uri/ /index.php?$args;
 }

 location ~ \.php$ {
 fastcgi_pass 127.0.0.1:9000;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 include fastcgi_params;
 }
 }
---
# === Pod Disruption Budget ===
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
 name: wordpress-pdb
 namespace: wordpress
spec:
 minAvailable: 2
 selector:
 matchLabels:
 app: wordpress
---
# === HPA — Auto Scaling ===
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
 name: wordpress-hpa
 namespace: wordpress
spec:
 scaleTargetRef:
 apiVersion: apps/v1
 kind: Deployment
 name: wordpress
 minReplicas: 3
 maxReplicas: 10
 metrics:
 - type: Resource
 resource:
 name: cpu
 target:
 type: Utilization
 averageUtilization: 70
 - type: Resource
 resource:
 name: memory
 target:
 type: Utilization
 averageUtilization: 80

Next.js Frontend เชื่อมกับ WordPress

WordPress Headless กับ Pod Scheduling — วิธีใช้
# === Next.js Frontend สำหรับ WordPress Headless ===
# lib/wordpress.ts

# // WordPress API Client
# const API_URL = process.env.WORDPRESS_API_URL || 'https://api.example.com'
# const GRAPHQL_URL = `/graphql`
#
# // GraphQL Query
# export async function getPosts(first = 10) {
# const query = `
# query GetPosts($first: Int!) {
# posts(first: $first, where: { status: PUBLISH }) {
# nodes {
# id
# slug
# title
# excerpt
# date
# featuredImage {
# node {
# sourceUrl
# altText
# }
# }
# categories {
# nodes {
# name
# slug
# }
# }
# }
# }
# }
# `
# const res = await fetch(GRAPHQL_URL, {
# method: 'POST',
# headers: { 'Content-Type': 'application/json' },
# body: JSON.stringify({ query, variables: { first } }),
# next: { revalidate: 60 }, // ISR: Revalidate every 60s
# })
# const json = await res.json()
# return json.data.posts.nodes
# }
#
# export async function getPostBySlug(slug: string) {
# const query = `
# query GetPost($slug: ID!) {
# post(id: $slug, idType: SLUG) {
# title
# content
# date
# modified
# seo {
# title
# metaDesc
# opengraphImage { sourceUrl }
# }
# categories { nodes { name slug } }
# tags { nodes { name } }
# }
# }
# `
# const res = await fetch(GRAPHQL_URL, {
# method: 'POST',
# headers: { 'Content-Type': 'application/json' },
# body: JSON.stringify({ query, variables: { slug } }),
# next: { revalidate: 60 },
# })
# const json = await res.json()
# return json.data.post
# }

# --- Python Script ทดสอบ WordPress REST API ---
import requests
import json

class WordPressAPIClient:
 """WordPress REST API Client"""

 def __init__(self, base_url, auth_token=None):
 self.base_url = base_url.rstrip("/")
 self.session = requests.Session()
 if auth_token:
 self.session.headers["Authorization"] = f"Bearer {auth_token}"

 def get_posts(self, per_page=10, page=1, status="publish"):
 resp = self.session.get(
 f"{self.base_url}/wp-json/wp/v2/posts",
 params={"per_page": per_page, "page": page, "status": status},
 )
 return resp.json()

 def get_post(self, slug):
 resp = self.session.get(
 f"{self.base_url}/wp-json/wp/v2/posts",
 params={"slug": slug},
 )
 posts = resp.json()
 return posts[0] if posts else None

 def get_categories(self):
 resp = self.session.get(f"{self.base_url}/wp-json/wp/v2/categories")
 return resp.json()

 def health_check(self):
 """ตรวจสอบสถานะ WordPress"""
 endpoints = [
 "/wp-json/wp/v2/posts?per_page=1",
 "/wp-json/wp/v2/categories",
 "/graphql",
 ]

 results = {}
 for ep in endpoints:
 try:
 resp = self.session.get(f"{self.base_url}{ep}", timeout=5)
 results[ep] = {"status": resp.status_code,
 "latency_ms": resp.elapsed.total_seconds() * 1000}
 except Exception as e:
 results[ep] = {"status": "error", "error": str(e)}

 print("WordPress Health Check:")
 for ep, r in results.items():
 status = r.get("status", "error")
 latency = r.get("latency_ms", 0)
 print(f" {ep}: {status} ({latency:.0f}ms)")

 return results

# wp = WordPressAPIClient("https://api.example.com")
# wp.health_check()
# posts = wp.get_posts(per_page=5)

Caching Strategy

# === Redis Cache สำหรับ WordPress Headless ===
# redis-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: redis
 namespace: wordpress
spec:
 replicas: 1
 selector:
 matchLabels:
 app: redis
 template:
 metadata:
 labels:
 app: redis
 spec:
 containers:
 - name: redis
 image: redis:7-alpine
 ports:
 - containerPort: 6379
 resources:
 requests:
 cpu: "100m"
 memory: 256Mi
 limits:
 cpu: "500m"
 memory: 1Gi
 command: ["redis-server", "--maxmemory", "512mb",
 "--maxmemory-policy", "allkeys-lru"]
---
# kubectl Commands สำหรับจัดการ

# ดู Pod Placement
kubectl get pods -n wordpress -o wide

# ดู HPA Status
kubectl get hpa -n wordpress

# ดู PDB Status
kubectl get pdb -n wordpress

# Scale Manual
kubectl scale deployment wordpress -n wordpress --replicas=5

# Rolling Update
kubectl set image deployment/wordpress wordpress=wordpress:6.6-php8.3-fpm \
 -n wordpress

# ดู Rollout Status
kubectl rollout status deployment/wordpress -n wordpress

# Rollback
kubectl rollout undo deployment/wordpress -n wordpress

Best Practices

  • Shared Storage: ใช้ NFS หรือ S3 สำหรับ wp-content/uploads เพื่อให้ทุก Pod เข้าถึงไฟล์เดียวกัน
  • Object Cache: ใช้ Redis Object Cache Plugin ลด Database Queries
  • CDN: ใช้ Cloudflare หรือ CloudFront Cache Static Assets และ API Responses
  • CORS: ตั้ง CORS Headers ให้ถูกต้อง อนุญาตเฉพาะ Frontend Domain
  • Security: ปิด XML-RPC, จำกัด wp-admin Access, ใช้ Application Password สำหรับ API
  • ISR (Incremental Static Regeneration): ใช้ Next.js ISR Revalidate Content ทุก 60 วินาที
  • Monitoring: ติดตาม Response Time, Error Rate, Cache Hit Rate, Pod Resource Usage

WordPress Headless คืออะไร

ใช้ WordPress เป็น Backend CMS ส่งข้อมูลผ่าน REST API หรือ GraphQL ไปให้ Frontend Framework เช่น Next.js แสดงผล ได้ WordPress Editor ที่คุ้นเคยและ Frontend ที่ทันสมัย

แนะนำเพิ่มเติม — ดูสัญญาณเทรดที่ XM Signal

เนื้อหาเกี่ยวข้อง — อ่านต่อ: Elixir Ecto MLOps Workflow

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง