กลับไปยังบทความ

DevOps

Kubernetes Ingress: routing ตาม host/path, IngressClass และ TLS ด้วย cert-manager

เข้าใจ Kubernetes Ingress ตั้งแต่ทำไมต้องใช้แทน LoadBalancer หลายตัว, Ingress กับ Ingress controller ต่างกันยังไง, IngressClass, routing ตาม host/path และการทำ TLS อัตโนมัติด้วย cert-manager

24 กันยายน 256910 นาทีในการอ่าน

kubernetes ingress routing and tls

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 IPTLS certificateRouting
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 ให้ส่งไป Service api port 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 หน้าตาแบบนี้:

text
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

yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: public
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: traefik.io/ingress-controller

Ingress อ้างถึง class ผ่าน spec.ingressClassName ส่วน annotation แบบเก่า kubernetes.io/ingress.class ถูก deprecate แล้ว ให้ใช้ field แทน Ingress ที่ไม่ระบุ class จะไปอยู่กับ default class ถ้ามีการตั้งไว้ ถ้าไม่มีก็อาจไม่มี controller ตัวไหนสนใจเลย

pattern ที่ใช้บ่อยคือมีสอง class แยก controller กันคนละชุด:

  • public รับ traffic จาก internet อยู่หลัง external load balancer
  • internal สำหรับเครื่องมือภายในและหน้า admin เข้าได้เฉพาะจาก network องค์กรหรือ VPN

การแยก class ยังช่วยกระจายโหลดด้วย แต่ละ controller มี replica และ load balancer ของตัวเอง domain ที่โหลดหนักจึงไม่ไปแย่งทรัพยากรของ domain อื่น

Routing ตาม host และตาม path

Ingress route ได้จากสองอย่าง คือ Host header และ URL path

yaml
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.com path /static ไป service หนึ่ง ที่เหลือทั้งหมดไป web

pathType สำคัญกว่าที่คิด:

pathTypeพฤติกรรม
Exactต้องตรงทุกตัวอักษร /api ไม่ match /api/
Prefixmatch ทีละ 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:

yaml
spec:
  ingressClassName: public
  tls:
    - hosts:
        - api.example.com
        - www.example.com
      secretName: shop-tls
  rules:
    # ... same rules as above

controller ใช้ 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:

yaml
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:

yaml
metadata:
  name: shop
  namespace: shop
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod

cert-manager จะเห็น block tls แล้วสร้าง resource Certificate ทำ HTTP-01 challenge ผ่าน Ingress controller ตัวเดียวกัน แล้วเขียนผลลง shop-tls ดูความคืบหน้าได้ด้วย:

bash
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 แต่ไม่จำเป็นต้องเป็นแบบนั้น:

  1. controller เป็นหนึ่งเดียวในเชิง logic แต่มีหลาย pod จริง รันเป็น Deployment หลาย replica หรือเป็น DaemonSet กระจายหลาย node แล้วให้ external load balancer กระจาย traffic ไปยัง pod เหล่านั้น
  2. ทำ autoscale ให้มัน ใส่ HPA ให้ Deployment ของ controller ได้เหมือนแอปทั่วไป ดูรายละเอียดใน Kubernetes autoscaling ด้วย HPA, VPA และ Cluster Autoscaler
  3. แยกด้วย IngressClass สำหรับ domain ที่โหลดหนัก หรือแยก public กับ internal
  4. มีแค่ 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 ฟรี ให้เรียน

พร้อมสร้างสิ่งที่ยิ่งใหญ่หรือยัง?

เล่าโปรเจคของคุณให้เราฟัง — เราจะพาคุณไปถึง

เลือกคุกกี้ที่เราใช้ได้ คุกกี้ที่จำเป็นช่วยให้เว็บไซต์ทำงานได้และเปิดใช้งานเสมอ