通過Service可以實現存取Kubernetes叢集中的一組Pod
及實現負載均衡 ,但Service
中的負載均衡都是基於IP和埠的四層負載均衡。而要想實現七層負載均衡,則需要引入Ingress
。Ingress
是對叢集中服務的外部存取進行管理的API物件,典型的存取方式是HTTP。Ingress
可以提供負載均衡、SSL和基於主機名的存取。
本文主要參考官方文件對Kubernetes的Ingress進行一個簡單總結。
本文所使用的環境如下:
Ingress公開了從叢集外部到叢集內的HTTP和HTTPS路由。流量路由由Ingress資源上定義的規則控制。
internet
|
[ Ingress ]
--|-----|--
[ Services ]
可以給Ingress設定爲服務提供外部可存取的URL、負載均衡流量、SSL/TLS以及提供基於名稱的虛擬主機等。Ingress控制器(Ingress Controller)通常負責通過負載均衡器來實現Ingress,儘管它也可以設定邊緣路由器或其他前端來幫助處理流量。Ingress不會公開任意埠或協定。將HTTP和HTTPS以外的服務公開到Internet時,通常使用Service.Type=NodePort
或Service.Type=LoadBalancer
型別的服務。
Ingress資源自身並不能進行「流量穿透」,它僅是一組路由規則的集合,這些規則要想真正發揮作用還需要其他功能的輔助,如監聽某通訊端,然後根據這些規則的匹配機制 機製路由請求流量。這種能夠爲Ingress資源監聽通訊端並轉發流量的元件稱爲Ingress控制器(Ingress Controller)。
爲了讓Ingress資源工作,叢集必須有一個正在執行的Ingress控制器。與作爲kube-controller-manager
可執行檔案的一部分執行的其他型別的控制器不同,Ingress控制器不是隨叢集自動啓動的,需要在叢集上單獨部署。Kubernetes專案目前支援和維護GCE和nginx控制器。至於其他控制器可以參考Ingress控制器官方文件。
可以在叢集中部署任意數量的ingress控制器,建立ingress時,應該使用適當的ingress.class
註解每個Ingress以表明在叢集中如果有多個Ingress控制器時,應該使用哪個Ingress控制器。如果不定義ingress.class
,雲提供商可能使用預設的Ingress控制器。
必須具有Ingress控制器才能 纔能滿足Ingress的要求。僅建立Ingress資源本身沒有任何效果。使用Ingress可能需要部署一個例如ingress-nginx的Ingress控制器。至於使用哪個Ingress控制器,可以從許多Ingress控制器中進行選擇。理想情況下,所有Ingress控制器都應符合參考規範。但實際上,不同的Ingress控制器操作略有不同。
如下圖所示,流量首先到達外部負載均衡器,這個負載均衡器是由我們自己部署的,然後轉發到Ingress控制器的Service
上,然後再轉發到Ingress控制器的Pod
上,通過Ingress控制器基於Ingress
資源定義的規則將用戶端請求流量直接轉發至與Service
對應的後端Pod
上。這種轉發機制 機製會繞過Service
,從而省去了由kube-proxy
實現的埠代理開銷,Ingress規則需要由一個Service
資源物件輔助識別相關的所有Pod
資源。
Ingress控制器自身是執行於Pod
中的容器應用,一般是例如Nginx這種具有代理及負載均衡功能的守護行程,它監視着來自API Server
的Ingress
物件狀態,並根據規則生成相應的應用程式專有格式的組態檔並通過過載或重新啓動守護行程而使新設定生效。Ingress控制器其實就是託管於Kubernetes系統之上的用於實現在應用層發佈服務的Pod
資源,跟蹤Ingress
資源並實時生成設定規則。
Ingress控制器可以通過兩種方式部署以接入外部請求流量。第一種方式是通過Deployment
控制器管理Ingress控制器的Pod
資源,通過NodePort
或LoadBalancer
型別的Service
物件爲其接入叢集外部的請求流量,所有這種方式在定義一個Ingress控制器時必須在其前端定義一個專用的Service
資源。如下圖所示:
第二種方式是通過DaemonSet
控制器確保叢集的所有或部分工作節點中每個節點上只執行Ingress控制器的一個Pod
資源,並設定這類Pod
物件以HostPort
或HostNetwork
的方式在當前節點接入外部流量。如下圖所示:
進入用於存放k8s相關檔案的目錄,這裏是/usr/local/k8s/
,建立一個用於ingress-nginx的目錄並進入:
cd /usr/local/k8s/
mkdir -p ingress-controller/ingress-nginx
cd ingress-controller/ingress-nginx/
下面 下麪就開始安裝ingress-nginx,可以參考ingress-nginx官方文件進行安裝,官方提供了多種平臺的安裝方式。由於這裏用的是自己使用kubeadm搭建的k8s環境,所以選擇Bare-metal的安裝組態檔進行安裝。下載v0.34.1
版的ingress-nginx的設定清單檔案並建立ingress-nginx:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml
kubectl apply -f deploy.yaml
ingress-nginx控制器的所有資源都在ingress-nginx
名稱空間下,檢視ingress-nginx
名稱空間中的Pod
及ingress-nginx控制器的Pod
的詳細資訊,這裏只擷取了部分資訊:
檢視ingress-nginx
名稱空間中的Service
及ingress-nginx-controller
的詳細資訊:
可知建立了一個NodePort
型別的Service
,名爲ingress-nginx-controller
,並隨機分配http
的NodePort
爲31681
,https
的NodePort
的爲31620
,對外暴露服務。
一個最小的Ingress資源範例:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
serviceName: test
servicePort: 80
與所有其他Kubernetes資源一樣,Ingress也需要使用apiVersion
、kind
和metadata
欄位。Ingress提供了設定負載均衡器或者代理伺服器所需的所有資訊,其中包含與所有傳入請求匹配的規則列表。Ingress資源僅支援用於轉發HTTP流量的規則。
IngressSpec
下的rules
用於設定Ingress的主機規則列表,如果未指定規則或未匹配到任何規則,則所有流量都會被轉發到預設後端。每個HTTP規則都包含以下資訊:
host
):在此範例中,未指定主機,因此該規則適用於通過指定IP地址的所有入站HTTP通訊。如果提供了主機,例如 foo.bar.com
,則規則適用於該主機。path
):例如/testpath
,每個路徑都有一個由serviceName
和servicePort
定義的關聯後端。在負載均衡器將流量定向到參照的服務之前,主機和路徑都必須匹配傳入的請求。backend
):是服務和伺服器端口的組合。與規則的主機和路徑匹配的對Ingress的HTTP(和HTTPS )請求將發送到列出的後端。沒有定義規則的Ingress將所有流量發送到同一個預設後端。預設後端通常是Ingress控制器的設定選項,並且未在Ingress資源中指定。如果主機或路徑都沒有與Ingress物件中的HTTP請求匹配,則流量將路由到預設後端。
Ingress中的每個路徑都有對應的路徑型別。當前支援以下三種路徑型別:
ImplementationSpecific
(預設):對於這種型別,匹配取決於IngressClass。具體實現可以將其作爲單獨的pathType
處理或者與Prefix
或Exact
型別作相同處理。Exact
:精確匹配URL路徑,且對大小寫敏感。Prefix
:基於以/
分隔的URL路徑字首匹配。匹配對大小寫敏感,並且對路徑中的元素逐個完成。路徑元素指的是由/
分隔符分隔的路徑中的標籤列表。注意:
- 如果路徑的最後一個元素是請求路徑中最後一個元素的子字串,則不會匹配 (例如:
/foo/bar
匹配/foo/bar/baz
,但不匹配/foo/barbaz
)。- 如果Ingress中的多條路徑匹配同一個請求。這種情況下最長的匹配路徑優先。 如果仍然有兩條同等的匹配路徑,則
Exact
路徑型別優先於Prefix
路徑型別。
Kubernetes允許暴露單個Service
,本文使用ingress-nginx控制器暴露服務,示意圖如下:
先建立一個名爲ingress-test1
的名稱空間,方便管理。建立namespace-ingress-test1.yaml
:
apiVersion: v1
kind: Namespace
metadata:
name: ingress-test1
labels:
env: ingress-test1
建立名稱空間ingress-test1
:
kubectl apply -f namespace-ingress-test1.yaml
檢視ingress-test1
名稱空間:
kubectl get namespace ingress-test1
部署nginx
範例,通過Deployment
控制器在ingress-test1
名稱空間中部署nginx
的Pod
,通過名爲mynginx-service
的service
資源的88
埠暴露容器的80
埠。先建立deploy-svc-test1.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mynginx-deployment
namespace: ingress-test1
labels:
app: MyNginx
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: MyNginx
version: v1
template:
metadata:
labels:
app: MyNginx
version: v1
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: mynginx-service
namespace: ingress-test1
spec:
type: ClusterIP
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
建立Deployment
和Service
:
kubectl apply -f deploy-svc-test1.yaml
檢視名稱空間ingress-test1
中的Deployment
、Service
和Pod
資訊:
kubectl get deployment -n ingress-test1
kubectl get pod -n ingress-test1 -o wide
kubectl get svc -n ingress-test1
kubectl describe svc mynginx-service -n ingress-test1
下面 下麪建立Ingress
資源,匹配mynginx-service
並將該Service
的88
埠暴露,這個Ingress的規則將域名myapp.nginx.com
的根目錄對映到mynginx-service
。先建立single-svc-ingress.yaml
:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: single-svc-ingress
namespace: ingress-test1
spec:
rules:
- host: myapp.nginx.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 88
建立名爲single-svc-ingress
的Ingress
資源:
kubectl apply -f single-svc-ingress.yaml
檢視名稱空間ingress-test1
中的Ingress
及single-svc-ingress
詳細資訊:
kubectl get ingress -n ingress-test1
kubectl describe ingress single-svc-ingress -n ingress-test1
檢視ingress-nginx控制器Pod中生成的nginx
組態檔:
kubectl exec ingress-nginx-controller- -n ingress-nginx -it -- /bin/sh
cat /etc/nginx/nginx.conf
這裏擷取了部分內容:
在本地和虛擬機器hosts
檔案中新增域名解析列表,新增叢集任意一個節點IP與域名對應即可:
192.168.1.16 myapp.nginx.com
192.168.1.17 myapp.nginx.com
192.168.1.18 myapp.nginx.com
存取myapp.nginx.com:31681
,31681
爲服務ingress-nginx-controller
對外暴露的http
存取的NodePort
:
驗證是否排程到後端的Pod
資源,檢視日誌:
基於名稱的虛擬主機支援將針對多個主機名的HTTP流量路由到同一IP地址上,即將不同的host名對映到不同的Service
。如下圖所示:
先建立一個名爲ingress-test2
的名稱空間,方便管理。建立namespace-ingress-test2.yaml
:
apiVersion: v1
kind: Namespace
metadata:
name: ingress-test2
labels:
env: ingress-test2
建立並檢視名稱空間ingress-test2
:
然後建立一組Nginx
和Tomcat
範例,先建立資源清單檔案deploy-svc-test2.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mynginx-deployment
namespace: ingress-test2
labels:
app: MyNginx
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: MyNginx
version: v1
template:
metadata:
labels:
app: MyNginx
version: v1
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: mynginx-service
namespace: ingress-test2
spec:
type: ClusterIP
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mytomcat-deployment
namespace: ingress-test2
labels:
app: MyTomcat
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: MyTomcat
version: v1
template:
metadata:
labels:
app: MyTomcat
version: v1
spec:
containers:
- name: tomcat
image: tomcat:8.0.53
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
- name: ajp
containerPort: 8009
---
apiVersion: v1
kind: Service
metadata:
name: mytomcat-service
namespace: ingress-test2
spec:
selector:
app: MyTomcat
version: v1
ports:
- name: http
port: 8080
targetPort: 8080
- name: ajp
port: 8009
targetPort: 8009
Tomcat映象用的較老的8.0.53
版本,因爲較新版本的Tomcat沒有index頁面,爲了方便測試,選擇了低版本。建立資源清單中定義的資源物件並檢視:
建立Ingress
資源,先建立name-virtual-host-ingress.yaml
:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
namespace: ingress-test2
spec:
rules:
- host: mynginx.test.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 88
- host: mytomcat.test.com
http:
paths:
- path: /
backend:
serviceName: mytomcat-service
servicePort: 8080
建立Ingress
資源物件並檢視:
在本地和虛擬機器hosts
檔案中新增域名解析列表,新增叢集任意一個節點IP與域名對應即可:
192.168.1.16 mynginx.test.com mytomcat.test.com
192.168.1.17 mynginx.test.com mytomcat.test.com
192.168.1.18 mynginx.test.com mytomcat.test.com
存取mynginx.test.com:31681
:
存取mytomcat.test.com:31681
:
根據請求的HTTP URI可以將流量從單個IP地址路由到多個服務。即根據請求URL的路徑將不同路徑的請求發送到不同的服務,用戶端可以通過一個Ingress控制器的IP地址存取到多個服務。如下圖所示:
注意:
Ingress
中path
的要與後端Service
提供的Path
一致,否則將被轉發到一個不存在的path
上,引起錯誤。
一個將不同的路徑發送到不同服務的Ingress
資源如下:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-ingress
namespace: ingress-test2
spec:
rules:
- host: myapp.test.com
http:
paths:
- path: /nginx
backend:
serviceName: mynginx-service
servicePort: 88
- path: /tomcat
backend:
serviceName: mytomcat-service
servicePort: 8080
可以通過設定包含TLS私鑰和證書的Secret
來保護Ingress
。目前,Ingress
只支援單個TLS埠443
。這裏使用openssl
生成自簽名證書:
#生成mynginx.test.com域名的證書
openssl genrsa -out mynginx.test.com.key 4096
openssl req -x509 -new -key mynginx.test.com.key -days 3650 -out mynginx.test.com.crt -subj /C=CN/ST=ChongQing/L=ChongQing/O=RtxTitanV/CN=mynginx.test.com
#生成mytomcat.test.com域名的證書
openssl genrsa -out mytomcat.test.com.key 4096
openssl req -x509 -new -key mytomcat.test.com.key -days 3650 -out mytomcat.test.com.crt -subj /C=CN/ST=ChongQing/L=ChongQing/O=RtxTitanV/CN=mytomcat.test.com
建立Secret
:
#建立mynginx.test.com域名的secret
kubectl create secret tls mynginx-ingress-secret --cert=mynginx.test.com.crt --key=mynginx.test.com.key -n ingress-test2
#建立mytomcat.test.com域名的secret
kubectl create secret tls mytomcat-ingress-secret --cert=mytomcat.test.com.crt --key=mytomcat.test.com.key -n ingress-test2
檢視Secret
:
kubectl get secret -n ingress-test2
建立帶TLS的Ingress
資源,先建立tls-ingress.yaml
:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-ingress
namespace: ingress-test2
spec:
tls:
- hosts:
- mynginx.test.com
secretName: mynginx-ingress-secret
- hosts:
- mytomcat.test.com
secretName: mytomcat-ingress-secret
rules:
- host: mynginx.test.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 88
- host: mytomcat.test.com
http:
paths:
- path: /
backend:
serviceName: mytomcat-service
servicePort: 8080
建立ingress
資源並檢視:
進行https
存取測試,通過ingress-nginx控制器的前端的Service
資源的對外暴露的NodePort
來存取此服務,而ingress-nginx控制器的Service
的443
埠對應的NodePort
爲31620
,下面 下麪存取https://mynginx.test.com:31620/
:
存取https://mytomcat.test.com:31620/
:
提示不安全是因爲證書是自簽名證書,不用管它。
首先安裝httpd:
yum install -y httpd
建立認證檔案auth
,並根據auth
檔案儲存到k8s的secret
中:
htpasswd -c auth rtxtitanv
kubectl create secret generic basic-auth --from-file=auth -n ingress-test1
建立basicauth-ingress.yaml
以新增需要認證的主機名:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: basicauth-ingress
namespace: ingress-test1
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - rtxtitanv'
spec:
rules:
- host: bashauth.nginx.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 88
建立ingress
資源並檢視:
下面 下麪進行存取測試,先在hosts
檔案新增域名解析,使bashauth.nginx.com
對映到k8s任意節點IP,然後存取bashauth.nginx.com:31681
,需要先輸入剛纔設定的使用者名稱和密碼:
Nginx重寫主要有以下幾種,都在annotations
下宣告:
名稱 | 描述 | 值 |
---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | 必須重定向流量的目標URL | 字串 |
nginx.ingress.kubernetes.io/ssl-redirect | 指示位置部分是否僅可存取SSL(當Ingress包含證書時預設爲True)布爾 | 布爾 |
nginx.ingress.kubernetes.io/force-ssl-redirect | 即使lngress未啓用TLS,也強制重定向到HTTPS | 布爾 |
nginx.ingress.kubernetes.io/app-root | 定義Controller必須重定向的應用程式根,如果它在‘/’上下文中 | 字串 |
nginx.ingress.kubernetes.io/use-regex | 指示lngress上定義的路徑是否使用正則表達式 | 布爾 |
建立Ingress
資源,使存取re.mynginx.com
的請求都將被重定向到myapp.nginx.com
。先建立rewrite-ingress.yaml
:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: rewrite-ingress
namespace: ingress-test1
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://myapp.nginx.com:31681
spec:
rules:
- host: re.mynginx.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 88
建立ingress
資源並檢視:
下面 下麪進行存取測試,先在hosts
檔案新增域名解析,使re.mynginx.com
對映到k8s任意節點IP,然後存取re.mynginx.com:31681
,會跳轉到myapp.nginx.com:31681
:
如果要更新現有的Ingress
以新增新的Host,可以通過編輯Ingress
資源來對其進行更新。先檢視名稱空間ingress-test1
中single-svc-ingress
的詳細資訊:
kubectl describe ingress single-svc-ingress -n ingress-test1
在更新之前先在ingress-test1
名稱空間中建立Tomcat
的Deployment
和Service
,建立過程參考上文,這裏就省略了。然後執行以下命令修改設定以更新Ingress
:
kubectl edit ingress single-svc-ingress -n ingress-test1
這一命令將開啓編輯器,如下修改設定來增加新的主機:
spec:
rules:
- host: myapp.nginx.com
http:
paths:
- backend:
serviceName: mynginx-service
servicePort: 88
path: /
pathType: ImplementationSpecific
- host: myapp.tomcat.com
http:
paths:
- backend:
serviceName: mytomcat-service
servicePort: 8080
path: /
pathType: ImplementationSpecific
儲存更改後,kubectl 將更新API伺服器中的資源,該資源將告訴Ingress控制器重新設定負載均衡器。驗證:
kubectl describe ingress single-svc-ingress -n ingress-test1
single-svc-ingress
的詳細資訊可知已經新增了一條Ingress
規則。下面 下麪進行存取測試,先在hosts
檔案新增域名解析,使myapp.tomcat.com
對映到k8s任意節點IP,然後存取myapp.tomcat.com:31681
,成功存取到Tomcat歡迎頁面,說明Ingress
更新成功:
Ingress可以由不同的控制器實現,通常使用不同的設定。每個Ingress應當指定一個Class,也就是一個對IngressClass資源的參照。 IngressClass資源包含額外的設定,其中包括應當實現該類的控制器名稱。例如:
apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: example.com/ingress-controller
parameters:
apiGroup: k8s.example.com/v1alpha
kind: IngressParameters
name: external-lb
IngressClass資源包含一個可選的參數欄位parameters
,可用於爲該Class參照額外設定。
在Kubernetes 1.18版本引入IngressClass資源和ingressClassName
欄位之前,IngressClass是通過Ingress中的一個kubernetes.io/ingress.class
註解來指定的。這個註解從未被正式定義過,但是得到了Ingress控制器的廣泛支援。Ingress中新的 ingressClassName
欄位用來替代該註解,但並非完全等價。該註解通常用於參照實現該Ingress的控制器的名稱,而這個新的欄位則是對一個包含額外Ingress設定的IngressClass資源的參照,包括Ingress控制器的名稱。
可以將一個特定的IngressClass標記爲叢集預設選項。將一個IngressClass資源的ingressclass.kubernetes.io/is-default-class
註解設定爲true
將確保新的未指定ingressClassName
欄位的Ingress能夠分配爲這個預設的IngressClass。注意,如果叢集中有多個IngressClass被標記爲預設,則準入控制器將阻止建立新的未指定ingressClassName
的Ingress物件。解決這個問題只需確保叢集中最多隻能有一個IngressClass被標記爲預設。