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
- 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 ที่ทันสมัย
ทำไมต้องรัน 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
