SiamCafe.net Blog
Technology

WordPress Headless Pod Scheduling

wordpress headless pod scheduling
WordPress Headless Pod Scheduling | SiamCafe Blog
2026-04-09· อ. บอม — SiamCafe.net· 9,073 คำ

WordPress Headless คืออะไร

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

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

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

# === 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

WordPress Headless คืออะไร

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

ทำไมต้องรัน WordPress บน Kubernetes

Scale อัตโนมัติ High Availability ด้วย Multiple Replicas Rolling Updates ไม่มี Downtime Resource Management ที่ดี Self-healing ถ้า Pod ตาย K8s สร้างใหม่ เหมาะกับ Traffic สูง

Pod Scheduling สำหรับ WordPress ต้องตั้งค่าอย่างไร

Pod Anti-affinity กระจาย Pods คนละ Node, Topology Spread ข้าม AZ, Resource Requests/Limits, PDB ป้องกันลบ Pod พร้อมกัน, Node Affinity สำหรับ SSD Node, HPA Auto Scaling

WPGraphQL คืออะไร

Plugin เพิ่ม GraphQL API ให้ WordPress Frontend Query ข้อมูลได้ยืดหยุ่น เลือกเฉพาะ Fields ที่ต้องการ ลด Over-fetching รองรับ Mutations สำหรับ Create/Update Content

สรุป

WordPress Headless บน Kubernetes ให้ทั้ง Content Management ที่คุ้นเคยและ Scalability ที่ดี ใช้ WPGraphQL สำหรับ API ที่ยืดหยุ่น Next.js สำหรับ Frontend ที่เร็ว Pod Scheduling ด้วย Anti-affinity และ Topology Spread สำหรับ HA, HPA สำหรับ Auto Scaling, Redis สำหรับ Object Cache และ CDN สำหรับ Static Assets

📖 บทความที่เกี่ยวข้อง

WordPress Headless Post-mortem Analysisอ่านบทความ → GraphQL Federation Pod Schedulingอ่านบทความ → WordPress Headless Security Hardening ป้องกันแฮกอ่านบทความ → WordPress Headless Agile Scrum Kanbanอ่านบทความ → Fivetran Connector Pod Schedulingอ่านบทความ →

📚 ดูบทความทั้งหมด →