--- lang: zh-tw --- 第八章 使用Ingress執行HTTP負載平衡 === 原本讓外部訪問svc的方式: NodePort --> 每個svc連接唯一的port LoadBalancer --> 每個svc都要對應一個LoadBalancer #### ingress可以使用單一的入口點來管理外部訪問 在Kubernetes中,基於HTTP的負載平衡系統稱為Ingress * 將其組態配置標準化 * 成為正規的Kubernetes物件 * 將多個Ingress物件合併到單一負載平衡器 ![](https://hackmd.io/_uploads/HyaDvb-Sn.png) 可以像建立Kubernetes其他物件一樣,建立或修改Ingress物件,但需安裝需要的控制器。 沒有一個HTTP LoadBalancer可以通用,AWS(ELB). GCP(Cloud Load Balancing)也都有提供LoadBalancer的功能,並提供各自的ingress controller去建立ingress。 ### 安裝 NGINX ingress controller 因為在本地只能用NodePort NGINX會用NodePort當作單一入口點,後續就透過ingress來做流量分流 ``` # install helm $ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 $ chmod 700 get_helm.sh $ ./get_helm.sh ``` ``` # install NGINX ingress controller using helm helm repo add nginx-stable https://helm.nginx.com/stable helm repo update helm install nginx-ingress nginx-stable/nginx-ingress --set rbac.create=true ``` ``` $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-ingress-controller-676fc965f7-f9r9h 1/1 Running 0 40m ``` ``` $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 105m nginx-ingress-controller LoadBalancer 10.111.242.125 localhost 80:31420/TCP,443:30138/TCP 44m ``` EXTERNAL-IP欄位,會是IP位址(GCP.Azure)或主機名(AWS) ### 配置DNS Ingress控制器將基於該主機名所傳入請求指向到對應的upstream service 外部負載平衡器是IP地址,則需要建立A record 外部負載平衡器是主機名,則需建立CNAME紀錄 如果沒有自有的域名,則可以透過編輯/etc/hosts檔案來新增IP地址以設定本機配置 ``` $ vi /etc/hosts ## # Host Database # # localhost is used to configure the loopback interface # when the system is booting. Do not change this entry. ## 127.0.0.1 web.example.com 127.0.0.1 localhost ``` ### 使用Ingress demo.yaml including namespace, deployment, service, ingress 來建立一個簡單的網站 note: ingress controller 可以跨namespace管理 ```yaml apiVersion: v1 kind: Namespace metadata: name: web --- apiVersion: apps/v1 kind: Deployment metadata: name: web-server namespace: web spec: selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: httpd image: httpd:2.4.53-alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: web-server-service namespace: web spec: selector: app: web ports: - protocol: TCP port: 5000 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-server-ingress namespace: web spec: ingressClassName: nginx rules: - host: web.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-server-service port: number: 5000 ``` ``` # deploy namespace, deployment, service, ingress $ kubectl apply -f demo.yaml ``` ``` $ kubectl get deployment -n web NAME READY UP-TO-DATE AVAILABLE AGE web-server 1/1 1 1 56m ``` ``` $ kubectl get svc -n web NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web-server-service ClusterIP 10.110.61.54 <none> 5000/TCP 56m ``` ``` $ kubectl get ingress -n web NAME CLASS HOSTS ADDRESS PORTS AGE web-server-ingress nginx web.example.com localhost 80 56m ``` ![](https://hackmd.io/_uploads/SJyJeXWS3.png) ### 使用多個主機名 本機配置 ``` $ vi /etc/hosts ## # Host Database # # localhost is used to configure the loopback interface # when the system is booting. Do not change this entry. ## 127.0.0.1 web.example.com purple.example.com green.example.com 127.0.0.1 localhost ``` demo-hosts.yaml 額外部署兩個服務 purple, green ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: purple-server namespace: web spec: selector: matchLabels: app: purple template: metadata: labels: app: purple spec: containers: - name: httpd image: gcr.io/kuar-demo/kuard-amd64:purple ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: purple-service namespace: web spec: selector: app: purple ports: - protocol: TCP port: 8080 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: green-server namespace: web spec: selector: matchLabels: app: green template: metadata: labels: app: green spec: containers: - name: httpd image: gcr.io/kuar-demo/kuard-amd64:green ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: green-service namespace: web spec: selector: app: green ports: - protocol: TCP port: 8080 targetPort: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: server-ingress namespace: web spec: ingressClassName: nginx rules: - host: purple.example.com http: paths: - path: / pathType: Prefix backend: service: name: purple-service port: number: 8080 - host: green.example.com http: paths: - path: / pathType: Prefix backend: service: name: green-service port: number: 8080 ``` ``` # deploy namespace, deployment, service, ingress $ kubectl apply -f demo-hosts.yaml ``` ``` $ kubectl describe ingress server-ingress -n web Name: server-ingress Labels: <none> Namespace: web Address: localhost Ingress Class: nginx Default backend: <default> Rules: Host Path Backends ---- ---- -------- purple.example.com / purple-service:8080 (10.1.0.15:8080) green.example.com / green-service:8080 (10.1.0.16:8080) Annotations: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 3m8s (x8 over 18m) nginx-ingress-controller Configuration for web/server-ingress was added or updated ``` ![](https://hackmd.io/_uploads/SyGdUX-S3.png) ![](https://hackmd.io/_uploads/SJpFLXWS2.png) ### 使用路徑 可以用來管理單一域名在不同路徑上管理多個服務 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-server-ingress namespace: web annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: web.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-server-service port: number: 5000 - path: /green pathType: Prefix backend: service: name: green-service port: number: 8080 ``` ``` $ kubectl describe ingress web-server-ingress -n web Name: web-server-ingress Labels: <none> Namespace: web Address: localhost Ingress Class: nginx Default backend: <default> Rules: Host Path Backends ---- ---- -------- web.example.com / web-server-service:5000 (10.1.0.13:80) /green green-service:8080 (10.1.0.16:8080) Annotations: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 5m49s (x13 over 124m) nginx-ingress-controller Configuration for web/web-server-ingress was added or updated ``` ### 清除 ``` $ kubectl delete -f demo.hosts.yaml ``` ``` $ kubectl get ingress -n web No resources found in web namespace. ``` ## 進階Ingress技巧 ### 運行多個Ingress控制器 在同一個cluster中運行多個ingress控制器,可以使用annotation配置 kubernetes.io/ingress.class ,這樣可以指定ingress物件要用哪一個控制器。 該值應該是一個string,這個值會指定哪些控制器應查看此物件。然後,應使用相同的值配置在控制器本身,並且應使用帶有正確annotation的ingress物件。 如果沒有配置 kubernetes.io/ingress.class 的annotation,則視為未定義。可能有多個控制器將爭先恐後地想取得ingress使用權,並寫入ingress物件的status欄位。 ### 多個Ingress物件 如果有多個ingress物件,則控制器會全部讀取他們,並嘗試將他們合併為同一個配置。但是,如果重複和衝突的配置,則會視為未定義的。但不同的控制器的行為可能會有所不同。 ### Ingress和Namespace ingress物件只能對應至相同namespace中的upstream service。 但controller可以部署在任何namespace都不受影響。 ### 路徑重寫 有些ingress控制器支援路徑重寫。可修改HTTP請求中的路徑。通常由ingress物件上的annotation配置,用於該物件指定的所有請求。不僅支援單純的路徑重寫,還支援正則表示式。 * 例如: 如果使用NGINX ingress控制器,則可指定annotation其主鍵是 nginx.ingress.kubernetes.io/rewrite-target:/ ,即使upstream service並沒有設計子路徑的處理。 但經常導致錯誤,如果要為了配合複雜的應用程式開發,最好避免使用子路徑。 ### 支援TLS 越來越需要使用TLS和HTTPS,首先需要使用TSL證書和密鑰建立一個secret物件。 上傳證書後,可以在ingress物件中引用他。如果多個ingress物件為同一主機名指定證書,則視為未定義的。 https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-multi-ssl#google-managed-certs Create GCP managed cert. ```yaml apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: FIRST_CERT_NAME spec: domains: - FIRST_DOMAIN --- apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: SECOND_CERT_NAME spec: domains: - SECOND_DOMAIN ``` Specifying certificates for your Ingress by GKE Ingress controller ```ymal apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-gmc-ingress annotations: networking.gke.io/managed-certificates: "FIRST_CERT_NAME,SECOND_CERT_NAME" spec: rules: - host: FIRST_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60001 - host: SECOND_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60002 ``` ### Ingress的替代實現方法 * 每個雲供應商都具有一個ingress實作 * NGINX ingress * Ambassador.Gloo * Traefik