Kubernetes Ingress คือชุดกฎ routing ระดับ HTTP ที่ทำให้หลาย service ใช้ทางเข้าคลัสเตอร์ร่วมกันได้ แทนที่จะเปิด LoadBalancer ให้ทุก service ที่ต้องออก public เราวาง load balancer ตัวเดียวไว้หน้า Ingress controller แล้วให้ controller ส่ง request ไปยัง Service ที่ถูกต้องตาม hostname และ path และชั้นนี้มักทำ TLS termination ไปด้วย certificate จึงอยู่ที่เดียว ไม่ต้องกระจายอยู่ในทุกแอป
บทความนี้อธิบายว่าทำไมต้องมี Ingress, Ingress object ต่างจาก Ingress controller ยังไง, IngressClass เลือก controller ยังไง, routing ตาม host และ path ทำงานยังไง และวิธีทำ HTTPS อัตโนมัติด้วย cert-manager รวมถึงสิ่งที่ Ingress ทำไม่ได้ และตำแหน่งของมันเมื่อเทียบกับ Gateway API
ทำไมต้องใช้ Kubernetes Ingress แทนการเปิด LoadBalancer หลายตัว
สมมติมี 10 service ที่ต้องเข้าถึงได้จาก internet ถ้าเปิดแต่ละตัวเป็น type: LoadBalancer ก็จะได้ external load balancer 10 ตัวและ public IP 10 ก้อน บน cloud แต่ละตัวคือค่าใช้จ่ายแยก ส่วนบน bare metal แต่ละตัวต้องให้ MetalLB (หรือเครื่องมือคล้ายกัน) จอง IP จาก pool ซึ่งปกติมีไม่มาก
ถ้าใช้ Ingress เราใช้ load balancer ตัวเดียวชี้ไปที่ Ingress controller แล้วให้ controller กระจาย traffic ไปทั้ง 10 service ตามกฎ:
| แนวทาง | External IP | TLS certificate | Routing |
|---|---|---|---|
LoadBalancer ต่อ service | หนึ่งก้อนต่อ service | จัดการในแต่ละแอปหรือแต่ละ LB | ไม่มี 1 IP ต่อ 1 service |
| Ingress | หนึ่งก้อน (หรือไม่กี่ก้อน) | รวมศูนย์ที่ Ingress | ตาม host, path และกฎเฉพาะของ controller |
ถ้ายังไม่ชัดว่า ClusterIP, NodePort และ LoadBalancer ต่างกันยังไง แนะนำให้อ่าน ประเภทของ Kubernetes Service ก่อน เพราะ Ingress ต่อยอดจากสิ่งเหล่านี้
Ingress object กับ Ingress controller ไม่ใช่สิ่งเดียวกัน
จุดนี้คนสับสนมากที่สุด มันคือสองสิ่งแยกกัน:
- Ingress object เป็นแค่ config คือ resource ใน API
networking.k8s.io/v1ที่บอกว่า "request ที่มาหาapi.example.com/v1ให้ส่งไป Serviceapiport 80" ถ้าคลัสเตอร์ไม่มี controller การสร้าง Ingress ก็ไม่มีผลอะไรเลย - Ingress controller คือ reverse proxy ที่รันเป็น pod ในคลัสเตอร์ เช่น Traefik, HAProxy Ingress, F5 NGINX Ingress Controller หรือ controller ของ cloud อย่าง AWS Load Balancer Controller มันคอย watch Ingress object ผ่าน API server แล้วปรับ config ตัวเองทุกครั้งที่กฎเปลี่ยน
เส้นทางของ request หน้าตาแบบนี้:
client -> external load balancer -> Ingress controller pods -> Service -> Podsเมื่อ controller เลือก backend ได้แล้ว ส่วนใหญ่จะส่ง traffic ตรงไปที่ pod IP ที่อ่านจาก EndpointSlice ของ Service ไม่ได้วิ่งผ่าน virtual IP ของ Service แต่ไม่ว่าแบบไหน pod ที่ได้รับ traffic ต้องผ่าน readiness probe แล้วเท่านั้น
เรื่องการเลือก controller: โปรเจกต์ ingress-nginx ของ community (kubernetes/ingress-nginx) ถูก retire แล้ว และช่วง maintenance แบบ best-effort สิ้นสุดเมื่อมีนาคม 2026 ตัวที่ติดตั้งอยู่ยังรันได้ แต่จะไม่มี bug fix หรือ security patch อีก ถ้าเริ่มใหม่ให้เลือก controller ที่ยังมีคนดูแล หรือไปใช้ Gateway API เลย ส่วนตัว Ingress API ยังเป็น stable และยังรองรับอยู่
IngressClass: กำหนดว่า controller ตัวไหนดูแล Ingress ไหน
คลัสเตอร์หนึ่งรัน controller ได้หลายตัว IngressClass เป็นตัวบอกว่า Ingress ไหนเป็นของ controller ไหน คล้ายกับที่ StorageClass เลือก storage provisioner
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: public
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: traefik.io/ingress-controllerIngress อ้างถึง class ผ่าน spec.ingressClassName ส่วน annotation แบบเก่า kubernetes.io/ingress.class ถูก deprecate แล้ว ให้ใช้ field แทน Ingress ที่ไม่ระบุ class จะไปอยู่กับ default class ถ้ามีการตั้งไว้ ถ้าไม่มีก็อาจไม่มี controller ตัวไหนสนใจเลย
pattern ที่ใช้บ่อยคือมีสอง class แยก controller กันคนละชุด:
publicรับ traffic จาก internet อยู่หลัง external load balancerinternalสำหรับเครื่องมือภายในและหน้า admin เข้าได้เฉพาะจาก network องค์กรหรือ VPN
การแยก class ยังช่วยกระจายโหลดด้วย แต่ละ controller มี replica และ load balancer ของตัวเอง domain ที่โหลดหนักจึงไม่ไปแย่งทรัพยากรของ domain อื่น
Routing ตาม host และตาม path
Ingress route ได้จากสองอย่าง คือ Host header และ URL path
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: public
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- host: www.example.com
http:
paths:
- path: /static
pathType: Prefix
backend:
service:
name: static-files
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 3000- Host-based:
api.example.comกับwww.example.comไปคนละ service แม้ทั้งคู่จะ resolve ไปที่ IP เดียวกัน - Path-based: ภายใต้
www.example.compath/staticไป service หนึ่ง ที่เหลือทั้งหมดไปweb
pathType สำคัญกว่าที่คิด:
| pathType | พฤติกรรม |
|---|---|
Exact | ต้องตรงทุกตัวอักษร /api ไม่ match /api/ |
Prefix | match ทีละ segment ของ path /api match /api และ /api/users แต่ไม่ match /apiv2 |
ImplementationSpecific | แล้วแต่ controller หลีกเลี่ยงไว้ถ้าไม่ได้ต้องการ matching แบบเฉพาะของ controller |
ถ้ามีหลาย path ที่ match พร้อมกัน path ที่ยาวที่สุดชนะ ดังนั้น /static จะชนะ / เสมอไม่ว่าจะเขียนเรียงแบบไหน และ Service ที่เป็น backend ต้องอยู่ namespace เดียวกับ Ingress
TLS termination บน Kubernetes Ingress
ปกติ Ingress จะเป็นจุด terminate HTTPS คือ controller ถือ certificate ไว้ decrypt request แล้วส่งต่อเป็น HTTP ธรรมดาเข้าไปหา service ในคลัสเตอร์ แอปไม่ต้องรู้เรื่อง certificate เลย
certificate เก็บอยู่ใน Secret ชนิด kubernetes.io/tls ใน namespace เดียวกับ Ingress แล้ว Ingress อ้างถึงใน spec.tls:
spec:
ingressClassName: public
tls:
- hosts:
- api.example.com
- www.example.com
secretName: shop-tls
rules:
# ... same rules as abovecontroller ใช้ SNI เลือก certificate ให้ถูกตัวเมื่อ controller เดียวให้บริการหลาย domain และ controller ส่วนใหญ่จะ redirect HTTP ไป HTTPS ให้เมื่อมีการตั้ง TLS แต่การเปิดปิดพฤติกรรมนี้เป็นเรื่องเฉพาะของแต่ละ controller
การใช้ HTTP ธรรมดาภายในคลัสเตอร์เป็น trade-off ที่ตั้งใจเลือก ถ้าต้องเข้ารหัสทุก hop ให้ re-encrypt ไปหา backend (ขึ้นกับ controller) หรือใช้ service mesh ที่ทำ mTLS
ออกและต่ออายุ certificate อัตโนมัติด้วย cert-manager
การต่ออายุ certificate ด้วยมือสุดท้ายจะมีวันลืม และมักเป็นวันหยุด cert-manager คอย watch Ingress object ขอ certificate จาก ACME issuer อย่าง Let's Encrypt เก็บลง Secret ตามชื่อที่เราระบุ และต่ออายุให้ก่อนหมดอายุ
เริ่มจากสร้าง issuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: publicจากนั้นใส่ annotation ให้ Ingress:
metadata:
name: shop
namespace: shop
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prodcert-manager จะเห็น block tls แล้วสร้าง resource Certificate ทำ HTTP-01 challenge ผ่าน Ingress controller ตัวเดียวกัน แล้วเขียนผลลง shop-tls ดูความคืบหน้าได้ด้วย:
kubectl get certificate -n shop
kubectl describe certificate shop-tls -n shopตอนทดสอบให้ใช้ staging server ของ Let's Encrypt เพราะ endpoint production มี rate limit ส่วน wildcard certificate ต้องใช้ DNS-01 solver เพราะ HTTP-01 พิสูจน์ความเป็นเจ้าของ *.example.com ไม่ได้
ฟีเจอร์ L7 และสิ่งที่ Ingress ทำไม่ได้
Ingress ทำงานที่ Layer 7 controller จึงอ่าน host, path และ header ของ HTTP ได้ นอกจาก routing พื้นฐานแล้ว controller ส่วนใหญ่มี:
- redirect HTTP ไป HTTPS และ HSTS
- rewrite path และตัด prefix ของ URL
- rate limiting และ IP allowlist
- external authentication (ส่งต่อไปเช็กกับ SSO)
- canary หรือ weighted routing ระหว่างสองเวอร์ชัน
- จำกัดขนาด request, timeout และ CORS header
ข้อควรระวังคือฟีเจอร์เกือบทั้งหมดนี้มาจาก annotation หรือ CRD เฉพาะของ controller ไม่ได้อยู่ใน spec ของ Ingress annotation ที่ใช้ได้กับ Traefik ไม่มีความหมายอะไรกับ HAProxy ควรเก็บ setting พวกนี้ไว้ในที่เดียว เวลาย้าย controller จะได้ไม่ต้องไล่ขุดทีละไฟล์
Ingress ยังมีข้อจำกัดชัด ๆ อีกสามข้อ:
- รองรับแค่ HTTP และ HTTPS ถ้าต้อง expose TCP หรือ UDP ตรง ๆ เช่น database, MQTT broker หรือ game server ต้องใช้ Service แบบ
LoadBalancerหรือNodePortหรือฟีเจอร์ TCP เฉพาะของ controller - header matching และ traffic splitting ไม่อยู่ใน spec ต้องพึ่ง controller
- resource เดียวปนหลายหน้าที่ ทีม platform ดูแล listener และ TLS ส่วนทีมแอปดูแล route แต่ทั้งหมดอยู่ใน object เดียวกัน
Gateway API (gateway.networking.k8s.io) แก้ทั้งสามข้อ โดยแยกเป็น GatewayClass, Gateway (listener และ TLS ทีม platform ดูแล) และ HTTPRoute (route ทีมแอปดูแล) และทำให้ header matching กับ weighted backend เป็นมาตรฐาน สำหรับ platform ใหม่นี่คือทิศทางที่ควรไป ส่วน Ingress ยังใช้ได้ดีกับ routing ตาม host และ path แบบตรงไปตรงมา
Ingress controller เป็นคอขวดหรือเปล่า
"ทุกอย่างวิ่งผ่าน Ingress ตัวเดียว" ฟังดูเหมือน single point of failure แต่ไม่จำเป็นต้องเป็นแบบนั้น:
- controller เป็นหนึ่งเดียวในเชิง logic แต่มีหลาย pod จริง รันเป็น Deployment หลาย replica หรือเป็น DaemonSet กระจายหลาย node แล้วให้ external load balancer กระจาย traffic ไปยัง pod เหล่านั้น
- ทำ autoscale ให้มัน ใส่ HPA ให้ Deployment ของ controller ได้เหมือนแอปทั่วไป ดูรายละเอียดใน Kubernetes autoscaling ด้วย HPA, VPA และ Cluster Autoscaler
- แยกด้วย IngressClass สำหรับ domain ที่โหลดหนัก หรือแยก public กับ internal
- มีแค่ traffic north-south ที่ผ่านมัน การเรียกกันระหว่าง service ภายในคลัสเตอร์วิ่งตรงผ่าน
ClusterIPไม่แตะ Ingress เลย และในระบบ microservices ส่วนใหญ่ traffic แบบนี้คือส่วนที่มากที่สุด
เรื่อง DNS, anycast และการวาง load balancer หลายตัวไว้หน้า Ingress controller หลายชุด อ่านต่อได้ที่ load balancing หลายชั้นด้วย DNS, anycast และ Ingress
คำถามที่พบบ่อย
Ingress กับ Ingress controller ต่างกันยังไง
Ingress คือ object ใน Kubernetes ที่เก็บกฎ routing ส่วน Ingress controller คือ proxy ที่อ่านกฎเหล่านั้นแล้วรับ traffic จริง ถ้าไม่มี controller ตัว Ingress object ก็ไม่มีผลอะไร
ใช้ Ingress แล้วยังต้องมี Service แบบ LoadBalancer อยู่ไหม
ส่วนใหญ่ยังต้องมี แต่แค่ตัวเดียว ตัว Ingress controller เองถูก expose ผ่าน LoadBalancer (หรือ NodePort ที่มี external load balancer อยู่หน้า) แล้วแอปทั้งหมดอยู่ข้างหลังเป็น Service แบบ ClusterIP
Kubernetes Ingress route TCP หรือ UDP ได้ไหม
spec มาตรฐานทำไม่ได้ Ingress มีไว้สำหรับ HTTP และ HTTPS ถ้าเป็น TCP หรือ UDP ให้ใช้ Service แบบ LoadBalancer, ฟีเจอร์ TCP เฉพาะของ controller หรือ route ของ Gateway API
ปี 2026 ควรใช้ Ingress หรือ Gateway API
ถ้าเป็น platform ใหม่ Gateway API เป็นตัวเลือกที่ดีกว่าในระยะยาว เพราะทำให้ฟีเจอร์ที่ Ingress ปล่อยให้เป็น annotation กลายเป็นมาตรฐาน ส่วน Ingress ที่ใช้อยู่ยังรองรับต่อไป สิ่งที่ควรรีบทำคือย้ายออกจาก controller ที่ถูก retire ไม่ใช่ย้ายออกจาก Ingress API
ทำไม certificate ของ cert-manager ค้างอยู่ที่ "not ready"
รัน kubectl describe กับ Certificate แล้วไล่ต่อไปที่ Order และ Challenge สาเหตุที่เจอบ่อยคือ DNS ยังไม่ชี้มาที่ Ingress, ตั้ง ingressClassName ใน HTTP-01 solver ผิด หรือชน rate limit ของ Let's Encrypt production
เช็กลิสต์สำหรับ Ingress
- controller หนึ่งชุดต่อหนึ่งประเภท traffic (
public,internal) แต่ละชุดมี 2 replica ขึ้นไปกระจายหลาย node - ทุก Ingress ระบุ
ingressClassNameชัดเจน - TLS ผ่าน cert-manager และทดสอบกับ staging issuer ก่อน
- ใช้ path แบบ
Prefixและไม่พึ่งลำดับของกฎ - ใช้ annotation เฉพาะ controller ให้น้อยที่สุด และเขียนเอกสารกำกับไว้
- มีแผนย้ายไป Gateway API ถ้ายังใช้
ingress-nginxที่ถูก retire แล้ว
ถ้าทำครบตามนี้ ทางเข้าเดียวก็รองรับได้หลายสิบ service โดยไม่กลายเป็นคอขวด ถ้าอยากลองลงมือกับ Ingress, TLS และ networking ส่วนอื่นของ Kubernetes จริง ๆ Vectorkub มี คอร์ส DevOps ฟรี ให้เรียน
