DEV Community

loading...
Cover image for LFS258 [9/15]: Ingress

LFS258 [9/15]: Ingress

Sawit M.
・5 min read

TL;DR


Ingress Overview

ใน article ก่อนหน้านี้ เราได้เรียนรู้เกี่ยวกับ Service ซึ่งใช้ในการ expose application ให้ client ที่อยู่นอก cluster ใช้งานได้ ในบทนี้เราจะเรียนรู้เกี่ยวกับ Ingress Controllers และ Rules ซึ่งทำหน้าที่เดียวกัน แต่ต่างกันที่ประสิทธิภาพ แทนที่จะต้องใช้ load balancer เป็นตัวช่วย Ingress สามารถทำแบบนั้นได้เลย ไม่ว่าจะเป็น route ตาม domain หรือ URI Path

Ingress Controllers ไม่ได้เป็นส่วนหนึ่งของ kube-controller-manager binary แต่มันจะมี controller เป็นของตัวเอง ซึ่งก็มีได้หลาย controller โดย controller จะใช้ Ingress Rules ในการจัดการกับ traffic ทั้งขาเข้าและขาออก

Ingress รองรับ 2 controllers หลัก คือ GCE และ nginx จริงๆ แล้ว tool อื่นที่ทำหน้าที่เป็น reverse proxy เช่น HAProxy และ Traefix ก็ใช้งานได้ โดยมันจะ route traffic มายัง Service ที่ on แบบ ClusterIP ได้เลยตาม Ingress Rule ที่เรา configure ไว้


Ingress Controller

Ingress Controller อยู่ใน networking.k8s.io/v1beta1 resource group เป็น daemon ที่ run ใน Pod ซึ่งจะคอย monitor การเปลี่ยนแปลงของ /ingresses บน kube-apiserver ถ้าหากพบการเปลี่ยนแปลง มันจะไป configure rules เพื่อ support ให้ โดย traffic ส่วนใหญ่จะเป็น HTTP วิธีนี้จะทำให้สามารถเข้าถึง Services ได้ โดยไม่ต้องสนใจว่า Pods นั้นถูก deploy ไว้ที่ node ไหน

เราสามารถ deploy Ingress Controller ได้มากกว่า 1 controller ใน cluster เดียวกัน traffic จะใช้ annotation ในการเลือก controller ทีละตัวจนกว่าจะพบตัวที่เหมาะสม

inbound-ingress


Nginx

การ deploy Nginx Controller สามารถดูตัวอย่างของหลากหลาย platform ได้จาก ingress-nginx

เราสามารถ configure Nginx Controller ได้ผ่านทาง ConfigMap และ Annotations แต่ถ้ายังไม่พอใจเราสามารถ configure จาก custom template ได้ด้วย

คุณสมบัติของ Nginx Controller

  • integrate กับ RBAC ได้ง่าย
  • ใช้ annotation kubernetes.io/ingress.class: "nginx"
  • ถ้าจะใช้เป็น L7 revere proxy ต้องระบุ proxy-real-ip-cidr ด้วย
  • ถ้าจะใช้ feature session affinity ต้อง bypass kube-proxy
  • ไม่ได้ใช้ conntrack ที่เกิดจาก DNAT ของ iptables
  • ถ้าจะให้ terminate TLS ให้ต้องระบุุ host field ด้วย

Google Load Balancer Controller (GLBC)

GLBC คือ GCE L7 load balancer controller ที่จัดการ external loadbalancers ด้วย Kubernetes Ingress API

ในสร้าง 1 Ingress ต้องมี cloud resource เหล่านี้

  1. Global Forwarding Rule: เป็นตัว manage VIP ของ Ingress
  2. TargetHttpProxy: เป็นตัว manage SSL certs และ proxy ระหว่าง VIP และ Backend
  3. URL Map: routing rules
  4. Backend Service: รวม หลายๆ Instance Groups เป็น 1 Service Node Port
  5. Instance Group: VM ที่ เป็น kubernetes node

Pipeline เป็นแบบนี้

pipeline

โดย

  • แต่ละ Backend Service ต้องทำการ health check ไปยัง NodePort ของ Service
  • แต่ละ port ของ Backend Service ต้องตรงกับ Instance Group
  • แต่ละ port ของ Backend Service จะถูก expose ผ่าน firewall บน GCE LB IP ranges (130.211.0.0/22 and 35.191.0.0/16)

ตอนนี้ Ingress สามารถใช้ TLS และ TLS termination ที่ port 443 และยังไม่ support SNI


Ingress API Resources

Ingress object ยังเป็น extension API มีตัวอย่าง YAML file ดังนี้

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ghost
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          serviceName: test
          servicePort: 80

เราสามารถ manage มันได้ดังนี้

$ kubectl get ingress
$ kubectl delete ingress <ingress_name>
$ kubectl edit ingress <ingress_name>


Deploying the Ingress Controller

ในการ deploy Nginx Ingress ทำได้ง่ายๆ โดย ดูได้จาก Github

ในตัวอย่างจะขอ install แบบ Bare-Metal โดยใช้ NodePort ดังนี้

# Install
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
limitrange/ingress-nginx created
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
service/ingress-nginx created

# Verify
$ kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch
NAMESPACE       NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx   nginx-ingress-controller-7f74f657bd-9fm44   1/1     Running   0          82s


Creating an Ingress Rule

  1. ทำการ create deployment และ expose service ที่ port 2368

    $ kubectl create deployment ghost --image=ghost
    deployment.apps/ghost created
    
    $ kubectl expose deployment ghost --port 2368
    service/ghost exposed
    
    $ kubectl get svc
    NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    service/ghost        ClusterIP   10.107.189.29   <none>        2368/TCP   11s
    service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    74d
    
    $ kubectl get pods
    NAME                         READY   STATUS    RESTARTS   AGE
    pod/ghost-577564ddbd-nfqrc   1/1     Running   0          178m
    
  2. ทำการสร้าง ingress rule โดย request ที่เข้ามา domain เป็น "ghost.192.168.99.100.nip.io" ให้ส่งไปที่ service ghost

    $ cat > ingress.yaml << EOF
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
        name: ghost
    spec:
        rules:
        - host: ghost.192.168.99.100.nip.io
        http:
            paths:
            - backend:
                serviceName: ghost
                servicePort: 2368
    EOF
    $ kubectl create -f ingress.yaml
    ingress.networking.k8s.io/ghost created
    
    $ kubectl get ingress
    NAME    HOSTS                         ADDRESS   PORTS   AGE
    ghost   ghost.192.168.99.100.nip.io             80      20s
    
    $ kubectl describe ingress/ghost
    Name:             ghost
    Namespace:        default
    Address:          10.107.151.119
    Default backend:  default-http-backend:80 (<none>)
    Rules:
    Host                         Path  Backends
    ----                         ----  --------
    ghost.192.168.99.100.nip.io  
                                    ghost:2368 (192.168.32.59:2368)
    Annotations:
    Events:
    Type    Reason  Age    From                      Message
    ----    ------  ----   ----                      -------
    Normal  CREATE  3m12s  nginx-ingress-controller  Ingress default/ghost
    Normal  UPDATE  2m40s  nginx-ingress-controller  Ingress default/ghost
    
  3. ทำการ test ดูซิว่าถ้า doamin เป็น ghost.192.168.99.100.nip.io จะวิ่งไปที่ service ghost หรือไม่ โดยถ้าไปใช่ต้องได้ page ที่มี title เป็น ghost

    $ curl -v -H 'host: ghost.192.168.99.100.nip.io' http://10.107.151.119:80 -o /dev/null
    <title>Ghost</title>
    


Multiple Rules

  1. ทำการ create deployment ชื่อ nginx และ expose service ที่ port 80

    $ kubectl create deployment nginx --image=nginx
    deployment.apps/nginx created
    
    $ kubectl expose deployment nginx --port 80
    service/nginx exposed
    
    $ kubectl get svc
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    ghost        ClusterIP   10.107.189.29   <none>        2368/TCP   12m
    kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    74d
    nginx        ClusterIP   10.106.200.39   <none>        80/TCP     29s
    
    $ kubectl get pods
    NAME                     READY   STATUS    RESTARTS   AGE
    ghost-577564ddbd-nfqrc   1/1     Running   0          3h11m
    nginx-86c57db685-vb6gk   1/1     Running   0          63s
    
  2. ทำการ update ingress rule โดย เพิ่มให้ request ที่มี domain เป็น "nginx.192.168.99.100.nip.io" ให้ส่งไปที่ service nginx

    $ cat > ingress-2.yaml << EOF
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
        name: ghost
    spec:
        rules:
        - host: ghost.192.168.99.100.nip.io
        http:
            paths:
            - backend:
                serviceName: ghost
                servicePort: 2368
        - host: nginx.192.168.99.100.nip.io
        http:
            paths:
            - backend:
                serviceName: nginx
                servicePort: 80
    EOF
    
    $ kubectl apply -f ingress-2.yaml
    ingress.networking.k8s.io/ghost configured
    
    $ kubectl get ingress
    NAME    HOSTS                                                     ADDRESS          PORTS   AGE
    ghost   ghost.192.168.99.100.nip.io,nginx.192.168.99.100.nip.io   10.107.151.119   80      10m
    
    $ kubectl describe ingress/ghost
    Name:             ghost
    Namespace:        default
    Address:          10.107.151.119
    Default backend:  default-http-backend:80 (<none>)
    Rules:
    Host                         Path  Backends
    ----                         ----  --------
    ghost.192.168.99.100.nip.io  
                                    ghost:2368 (192.168.32.59:2368)
    nginx.192.168.99.100.nip.io  
                                    nginx:80 (192.168.52.65:80)
    Annotations:
    kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"ghost","namespace":"default"},"spec":{"rules":[{"host":"ghost.192.168.99.100.nip.io","http":{"paths":[{"backend":{"serviceName":"ghost","servicePort":2368}}]}},{"host":"nginx.192.168.99.100.nip.io","http":{"paths":[{"backend":{"serviceName":"nginx","servicePort":80}}]}}]}}
    
    Events:
    Type    Reason  Age                  From                      Message
    ----    ------  ----                 ----                      -------
    Normal  CREATE  10m                  nginx-ingress-controller  Ingress default/ghost
    Normal  UPDATE  79s (x2 over 9m50s)  nginx-ingress-controller  Ingress default/ghost
    
  3. ทำการ test ดูซิว่า

    • ถ้า doamin เป็น ghost.192.168.99.100.nip.io จะต้องวิ่งไปที่ service ghost
    • ถ้า doamin เป็น nginx.192.168.99.100.nip.io จะต้องวิ่งไปที่ service nginx
    $ curl -s -H 'host: ghost.192.168.99.100.nip.io' http://10.107.151.119:80 | grep '<title>'
    <title>Ghost</title>
    
    $ curl -s -H 'host: nginx.192.168.99.100.nip.io' http://10.107.151.119:80 | grep '<title>'
    <title>Welcome to nginx!</title>
    


Intelligent Connected Proxies

ในกรณีที่ต้องการ feaature ที่เพิ่มขึ้น เช่น service discovery, rate limiting, traffic management หรือ advanced metric เราต้องเปลี่ยนมาใช้ service mesh โดย มันจะประกอบด้วย edge proxy และ embeded proxy ซึ่งจะทำงานร่วมกันเพื่อจักการ traffic ตาม rules ที่มาจาก control plane ตัวอย่างของ service mesh เช่น

  • Envoy: เป็น proxy ที่มีการออกแบบแบบ modular และ extensible มักถูกใช้เป็น data plane ของ tool อื่นๆ ของ service mesh
  • Istio: เป็น tool ที่ช่วยเพิ่มความสามารถของ Envoy ด้วยการมี control plane ได้มากกว่า 1 ตัว เป็นตัวที่ทำให้ service mesh flexible ขึ้นและมี feature มากขึ้น

    istio-envoy

  • Linkerd: เป็นอีกหนึ่ง service mesh ที่ออกแบบมาให้่ใช้งานได้ง่าย เร็ว และ เบา

Discussion (0)