1.什么是 Ingress
NotePort
,
LoadBalance
, 此外
externalIPs
也可以使各類service對外提供服務,但是當集群服務很多的時候,NodePort方式最大的缺點是會占用很多集群機器的端口;LB方式最大的缺點則是每個service一個LB又有點浪費和麻煩,並且需要k8s之外的支持; 而ingress則只需要一個NodePort或者一個LB就可以滿足所有
service
對外服務的需求。工作機制大致可以用下圖表示:

提示:如上圖所示,ingress就是ingress 控制器pod的代理規則;用戶請求某個后端pod所提供的服務時,首先會通過ingress controller pod把流量引入到集群內部,然后ingress controller pod根據ingress定義的規則,把對應ingress規則轉化為對應ingress controller pod實現的對應應用的配置(ingress controller 可以由任何具有七層反向代理功能的服務實現,比如nginx,haproxy等等)然后再適配用戶請求,把對應請求反代到對應service上;而對於pod的選擇上,ingress控制器可以基於對應service中的標簽選擇器,直接同pod直接通信,無須通過service對象api的再次轉發,從而省去了用戶請求到kube-proxy實現的代理開銷(本質上ingress controller 也是運行為一個pod,和其他pod在同一網段中)。
Ingress 可以給 Service 提供集群外部訪問的 URL、負載均衡、SSL 終止、HTTP 路由等。為了配置這些 Ingress 規則,集群管理員需要部署一個 Ingress Controller,它監聽 Ingress 和 Service 的變化,並根據規則配置負載均衡並提供訪問入口。
Ingress 控制器和k8s上的其他控制不一樣,ingress控制器並不能直接運行為kube-controller-manager的一部分,它類似k8s集群上的coredns,需要在集群上單獨部署,本質上就是一個pod,我們可以使用k8s上的ds或deploy控制器來創建它。
2.Ingress的核心組件
要理解ingress,需要區分兩個概念,ingress和ingress-controller:
ingress controller
:核心是一個deployment,實現方式有很多,比如nginx, Contour, Haproxy, trafik, Istio,需要編寫的yaml有:Deployment, Service, ConfigMap, ServiceAccount(Auth),其中service的類型可以是NodePort或者LoadBalancer。具體是實現反向代理及負載均衡的程序,對ingress定義的規則進行解析,根據配置的規則來實現請求轉發;簡單來說,Ingress-controller才是負責轉發的組件,通過各種方式將他暴露在集群入口,外部對集群的請求流量會先到Ingress-controller,而Ingress對象是用來告訴Ingress-controller該如何轉發請求,比如那些域名那些path要轉發到那些服務等.ingress
:這個就是一個類型為Ingress
的k8s api對象了,一般用yaml配置,作用是定義請求如何轉發到service的規則,可以理解為配置模板。
假設已經有兩個服務部署在了k8s集群內部:
$ kubectl get svc,deploy NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/coffee-svc ClusterIP <none> <none> 80/TCP 1m svc/tea-svc ClusterIP <none> <none> 80/TCP 1m NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deploy/coffee 2 2 2 2 1m deploy/tea 1 1 1 1 1m
配置 Ingress resources,即可實現多個service對外暴露服務:
1)coffee-svc
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: cafe-ingress spec: rules: # 配置七層域名 - host: foo.bar.com http: paths: # 配置Context Path - path: /tea backend: serviceName: tea-svc servicePort: 80 # 配置Context Path - path: /coffee backend: serviceName: coffee-svc servicePort: 80
2)tea-svc
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: cafe-ingress spec: rules: # 配置七層域名 - host: tea.foo.bar.com http: paths: - backend: serviceName: tea-svc servicePort: 80 - host: coffee.foo.bar.com http: paths: - backend: serviceName: coffee-svc servicePort: 80
接着在hosts文件中添加一條解析規則即可在瀏覽器訪問了。
3.部署Nginx Ingress Controller( 基於Nginx 來處理請求)
資料信息地址:
Ingress-Nginx-github 地址:https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方地址:https://kubernetes.github.io/ingress-nginx
注意:選擇合適的版本
3.1下載並修改配置文件
下載整合配置文件,獲取配置文件地址:https://github.com/kubernetes/ingress-nginx/tree/nginx-0.30.0/deploy/static
# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
# service-nodeport.yaml為ingress通過nodeport對外提供服務,注意默認nodeport暴露端口為隨機,可以編輯該文件自定義端口。
# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
應用yml文件創建ingress資源:
# kubectl apply -f mandatory.yaml
# kubectl apply -f service-nodeport.yaml
# kubectl get all -n ingress-nginx -o wide
3.2實際應用舉例
3.2.1創建svc及后端deployment
# cat test-ingress-pod.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: web-a name: web-a spec: replicas: 2 selector: matchLabels: app: web-a template: metadata: labels: app: web-a spec: containers: - image: nginx name: web-a --- apiVersion: v1 kind: Service metadata: name: web-a labels: app: web-a spec: ports: - port: 80 targetPort: 80 selector: app: web-a
應用yaml:
# kubectl apply -f test-ingress-pod.yaml # kubectl get pod NAME READY STATUS RESTARTS AGE web-a-89b89d4dd-chhhq 1/1 Running 0 38s web-a-89b89d4dd-tjh82 1/1 Running 0 38s
查看service:
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d web-a ClusterIP 10.96.214.85 <none> 80/TCP 64s
3.2.2創建Ingress規則
其實就是配置NGINX的一個過程
# ingress規則中,要指定需要綁定暴露的svc名稱 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-web-a annotations: kubernetes.io/ingress.class: "nginx" # 指定 Ingress Controller 的類型 nginx.ingress.kubernetes.io/use-regex: "true" # 指定我們的 rules 的 path 可以使用正則表達式 nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" # 連接超時時間,默認為 5s nginx.ingress.kubernetes.io/proxy-send-timeout: "600" # 后端服務器回轉數據超時時間,默認為 60s nginx.ingress.kubernetes.io/proxy-send-timeout: "600" # 后端服務器響應超時時間,默認為 60s nginx.ingress.kubernetes.io/proxy-body-size: "10m" # 客戶端上傳文件,最大大小,默認為 20m spec: rules: #定義路由規則 - host: www.web-a.com # 主機名,只能是域名,修改為你自己的 http: paths: - path: / backend: serviceName: web-a # 后台部署的 Service Name servicePort: 80 # 后台部署的 Service Port
說明:在ingress配置中,annotations很重要,前面有說ingress-controller有很多不同的實現,而不同的Ingress-controller就可以根據"kubernetes.io/ingress.class"來判斷要使用那些ingress配置,同時,不同的ingress-controller也有對應的annotations配置,用於自定義一些參數。
應用yaml文件:
# kubectl apply -f test-ingress-web-a.yaml kubectl get ingress Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-web-a <none> www.web-a.com 10.96.148.17 80 6m26s
c:\windows\system32\drivers\etc\hosts:
192.168.0.25 www.web-a.com
訪問測試:域名+nodeport的模式

總結:
- 上面我們創建一個針對於nginx的deployment資源,pod為2個;
- 為nginx的pod暴露service服務,名稱為web-a
- 通過ingress把nginx暴露出去
這里對於nginx創建的svc服務,其實在實際調度過程中,流量是直接通過ingress然后調度到后端的pod,而沒有經過svc服務,svc只是提供一個收集pod服務的作用。
4.Ingress的部署,架構簡談
Ingress的部署,需要考慮兩個方面:
# 1 . ingress-controller是作為Pod來運行的,以什么方式部署比較好
# 2 . ingress解決了如何請求路由到集群內部,那他自己怎么暴露給外部比較好
下面列舉一些目前常用的部署和暴露方式,具體使用哪種方式還得根據實際需要考慮來決定:
- Deployment+LoadBalancer模式的service
如果要把ingress部署在公有雲,那用這種方式比較合適。用Deployment部署ingress-controller,創建一個type為LoadBalancer的service關聯這組pod。大部分公有雲,都會為LoadBalancer的service自動創建一個負載均衡器,通常還綁定了公網地址。只要把域名解析指向該地址,就實現了集群服務的對外暴露。
- Deloyment+NodePort模式的service
同樣用deployment模式部署ingress-controller,並創建對應的服務,但是type為NodePort。這樣,ingress就會暴露在集群節點ip的特定端口上。由於nodeport暴露的端口是隨機端口,一般會在前面再搭建一套負載均衡器來轉發請求。該方式一般用於宿主機是相對固定的環境ip地址不變的場景。
NodePort方式暴露ingress雖然簡單方便,但是NodePort多了一層NAT,在請求量級很大時可能對性能會有一定影響。
- DaemonSet+HostNetwork+nodeSelector
用DaemonSet結合nodeselector來部署ingress-controller到特定的node上,然后使用HostNetwork直接把該pod與宿主機node的網絡打通,直接使用宿主機的80/433端口就能訪問服務。這時,ingress-controller所在的node機器就很類似傳統架構的邊緣節點,比如機房入口的nginx服務器。該方式整個請求鏈路最簡單,性能相對NodePort模式更好。缺點是由於直接利用宿主機節點的網絡和端口,一個node只能部署一個ingress-controller pod。比較適合大並發的生產環境使用。
4.1.DaemonSet+HostNetwork+nodeSelector部署
DaemonSet簡介:
DaemonSet 是指在 Kubernetes 集群里運行一個 Daemon Pod, 它有如下特征:
- 這個 Pod 默認運行在 Kubernetes 集群里的每一個Node節點上;
- 每個節點上只有一個這樣的 Pod 實例;
- 當有新的節點加入集群時,會自動創建該 Pod 。
修改mandatory.yaml,主要修改pod相關:
apiVersion: apps/v1 kind: DaemonSet #修改為DaemonSet metadata: name: nginx-ingress-controller namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: #replicas: 1 #注銷此行,DaemonSet不需要此參數 selector: matchLabels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx template: metadata: labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx annotations: prometheus.io/port: "10254" prometheus.io/scrape: "true" spec: # wait up to five minutes for the drain of connections terminationGracePeriodSeconds: 300 serviceAccountName: nginx-ingress-serviceaccount hostNetwork: true #添加該字段讓docker使用物理機網絡,在物理機暴露服務端口(80),注意物理機80端口提前不能被占用 dnsPolicy: ClusterFirstWithHostNet #使用hostNetwork后容器會使用物理機網絡包括DNS,會無法解析內部service,使用此參數讓容器使用K8S的DNS nodeSelector: #添加節點標簽,在符合標簽的node上部署,所以運行此服務的node標簽要打上這個標簽 kubernetes.io/ingress-controller-ready: "true" tolerations: #添加對指定node節點污點的容忍度,和node節點的污點有關系,pod指定了容忍度后,就可以在對應的打了此污點的node上部署。 - key: "node-role.kubernetes.io/master" operator: "Equal" value: "" effect: "NoSchedule" containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0 ……
修改參數如下:
- kind: Deployment #修改為DaemonSet
- replicas: 1 #注銷此行,DaemonSet不需要此參數
- hostNetwork: true #添加該字段讓docker使用物理機網絡,在物理機暴露服務端口(80),注意物理機80端口提前不能被占用
- dnsPolicy: ClusterFirstWithHostNet #使用hostNetwork后容器會使用物理機網絡包括DNS,會無法解析內部service,使用此參數讓容器使用K8S的DNS
- nodeSelector:kubernetes.io/ingress-controller-ready: "true" #添加節點標簽,在符合標簽的node上部署,所以運行此服務的node標簽要改成這個標簽
- tolerations: 添加對指定node節點的污點容忍度,和node節點的污點有關系,pod指定了容忍度后,就可以在對應的打了此污點的node上部署。

# kubectl label nodes k8snode01 kubernetes.io/ingress-controller-ready=true # kubectl label nodes k8snode02 kubernetes.io/ingress-controller-ready=true
在查看:

給node02、node02打上污點,污點內容根據上述配置文件中tolerations定義的來進行:這一步主要的作用是限定了只有容忍這個污點的pod才可以在這個節點上部署,也就是只有Ingress才能在這個node上部署。
# kubectl taint nodes k8snode01 node-role.kubernetes.io/master=:NoSchedule # kubectl taint nodes k8snode02 node-role.kubernetes.io/master=:NoSchedule 說明:因為value是空的,所以=后面直接就是:NoSchedule
查看污點:
# kubectl describe nodes k8snode01 Name: k8snode01 Roles: <none> Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=linux kubernetes.io/arch=amd64 kubernetes.io/hostname=k8snode01 kubernetes.io/ingress-controller-ready=true kubernetes.io/os=linux Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"3a:bc:8d:c3:5f:47"} flannel.alpha.coreos.com/backend-type: vxlan flannel.alpha.coreos.com/kube-subnet-manager: true flannel.alpha.coreos.com/public-ip: 192.168.0.27 kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock node.alpha.kubernetes.io/ttl: 0 volumes.kubernetes.io/controller-managed-attach-detach: true CreationTimestamp: Sun, 05 Sep 2021 04:26:07 -0400 Taints: node-role.kubernetes.io/master:NoSchedule Unschedulable: false
……
部署Ingress:
# kubectl apply -f mandatory.yaml # kubectl get pod -n ingress-nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-ingress-controller-hpnz8 1/1 Running 0 7m36s 192.168.0.28 k8snode02 <none> <none> nginx-ingress-controller-pjxr8 1/1 Running 0 7m36s 192.168.0.27 k8snode01 <none> <none>
測試:
ingress規則還是使用前面我們創建的。
在win主機上直接解析,IP地址為node01或node02任意節點ip即可,訪問的時候也無需再加端口: