概述
向外網暴露叢集內服務,以使客戶端能夠訪問,有以下幾種方法,本文重點描述Ingress。
LoadBalancer
LoadBalancer一般由雲服務供應商提供或者使用者自定義,執行在叢集之外。在建立service時為其配置LoadBalancer相關引數,當從外網訪問叢集內servcie時,使用者直接連線到LoadBalancer伺服器,LoadBalancer伺服器再將流量轉發到叢集內service。Loadbalancer配置及使用方法與各雲服務供應商有關,本文不詳細描述。
NodePort
這種方式要求叢集中部分節點有被外網訪問的能力。Kubernetes為每個NodePort型別的服務在叢集中的每個節點上分配至少一個主機網路埠號。客戶通過能被外網訪問的節點IP加上節點埠的方式訪問服務。大多數情況下不會通過這種方式向叢集外暴露服務,原因有四。
其一:大多情況下,為了安全起見,叢集中的節點位於完全內網環境中,不應該有被外網直接訪問的能力。一般外網訪問叢集中的節點都是通過邊界伺服器如閘道器、跳板等,而這種邊界伺服器需要通過各種方式進行安全加固。
其二:如果叢集內節點可以從外網直接訪問的話,則會將叢集內節點地址、服務名稱、埠號等資訊直接暴露在外,非常不安全。
其三:服務埠號一般由系統自動分配,並非固定,而服務名稱也可能發生變更,此時外部客戶端需要跟蹤變更並修改,屬於重試耦合。
其四:這種方式,每個服務至少向外網暴露一個埠號,當服務很多時不易於管理。
Ingress
Ingress不是某種產品、元件的名稱,它應該是kubernetes向叢集外暴露服務的一種思路、技術,使用者完全可以根據這種思路提供自己的Ingress實現,當然kubernetes提供了預設Ingress實現還有其它第三方實現,一般無需自己開發。它的思路是這樣的,首先在叢集內執行一個服務或者pod也可以是容器,不管是什麼它至少應該有一個外網可以訪問的IP,至少向外網開放一個埠號,讓它充當反向代理伺服器。當外網想要訪問叢集內service時,只需訪問這個反向代理伺服器並指定相關引數,代理伺服器根據請求引數並結合內部規則,將請求轉發到service。這種思路與LoadBalancer的不同之處是它就位於叢集內,而LoadBalancer位於叢集外。與NodePort的不同之處是叢集只向外暴露一個服務或者pod等,而NodePort是暴露全部service。
Kubernetes用nginx實現反向代理伺服器,稱為Ingress Controller,是pod型別資源。同時提供了Ingress型別物件,通過建立Ingress物件配置nginx反向代理伺服器的轉發規則。Nginx反向代理伺服器收到來自外網的請求後,用請求的URL地址、請求頭欄位區別不同service,然後轉發請求。
部署Ingress Controller
在Kubernetes中,Ingress Controller典型是pod型別資源,其部署方式與普通pod相同,通過Deployment、DaemonSet等副本控制器部署,其中更值推薦的是DaemonSet方式。Ingress Controller需要部署在具備連通外網能力的節點上,首先在目標節點打上Ingress Controller專用標籤,然後在DaemonSet的配置檔案中配置節點選擇器選中此類標籤,控制pod例項可以部署的節點,通過為節點增減相關標籤控制Ingress Controller的pod例項個數。Ingress Controller一般佔用兩個節點主機埠,http用80,https用443。詳細參考:這裡。外網通過http://節點外網ip:80/...或者https://節點外網ip:80/...就可以訪問內部服務了,當前首先需要建立Ingress物件配置訪問策略。
建立Ingress物件
本節通過建立各種Ingress物件,展示Ingress的各種典型用法。
Single Service Ingress
配置檔案:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: testsvc
servicePort: 80
然後通過kubectl create -f
建立物件,這同建立一般物件並沒有很多區別,前面已經多次提到過,這裡不再詳細描述
檢視物件:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test-ingress - testsvc:80 107.178.254.228
以上配置中沒有具體的rule,所以諸如http(s)://107.178.254.228/xxx之類的請求都轉發到testsvc的80埠。
其於URL轉發(Simple fanout)
假如要實現以下目標:
foo.bar.com -> 178.91.123.132 -> / foo s1:80
/ bar s2:80
其中foo.bar.com是http請求體頭部中的host欄位,178.91.123.132是Ingress Controller外網地址,當請求路徑與/foo匹配時轉發到s1服務的80埠,當與/bar匹配時轉發到s2服務的80埠,其最核心邏輯是用URL區分不同服務。
配置如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: s1
servicePort: 80
- path: /bar
backend:
serviceName: s2
servicePort: 80
檢視物件:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test -
foo.bar.com
/foo s1:80
/bar s2:80
基於名稱的虛擬主機(Name based virtual hosting)
實現如下目錄:
foo.bar.com --| |-> foo.bar.com s1:80
| 178.91.123.132 |
bar.foo.com --| |-> bar.foo.com s2:8
這種方式的核心邏輯是用http請求中的host欄位區分不同服務,而不是URL。如host: foo.bar.com的請求被轉發到s1服務80埠,如host: bar.foo.com的請求被轉發到s2服務80埠。
配置:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: s1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: s2
servicePort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: s1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: s2
servicePort: 80
TLS
利用Secret型別物件為Ingress Controller提供私鑰及證書,對通訊鏈路加密。
Secret配置:
apiVersion: v1
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
kind: Secret
metadata:
name: testsecret
namespace: default
type: Secret
在Ingress物件中引用
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: no-rules-map
spec:
tls:
- secretName: testsecret
backend:
serviceName: s1
servicePort: 80
** 更新Ingress物件**
使用kubectl edit命令編輯Ingress實時物件:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test - 178.91.123.132
foo.bar.com
/foo s1:80
$ kubectl edit ing test
在彈出的編輯器中修改:
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: s1
servicePort: 80
path: /foo
- host: bar.baz.com
http:
paths:
- backend:
serviceName: s2
servicePort: 80
path: /foo
..
儲存關稍後確認更新:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test - 178.91.123.132
foo.bar.com
/foo s1:80
bar.baz.com
/foo s2:80
參考:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test - 178.91.123.132
foo.bar.com
/foo s1:80
bar.baz.com
/foo s2:80
Ingress-NginX傳遞自定義header
問題現場:
配置好Ingress之後可以通過Ingress正常訪問系統,但是輸入使用者名稱密碼之後登陸失敗。但是通過NodePort暴露服務時可以正常訪問和登入。接下來進過除錯發現是在獲取使用者資訊時出錯,無法從Request header中取到自定義的使用者資訊欄位。
參考此文章發現,NginX預設會將使用者自定義的header過濾掉,除非開啟 underscores_in_headers ,經過測試,在NginX中開啟 underscores_in_headers 之後系統登入正常。那麼如何在Ingress-NginX中開啟此項呢:
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app: ingress-nginx
data:
enable-underscores-in-headers: "true"