Ingress是管理外部網咯訪問K8S集羣中Service的API對象(典型就是HTTP),它可以提供負載均衡、SSL終端以及基於名字的虛擬host。簡單點來說它就是外網訪問集羣應用的媒介,流程如下:
+----------+ Ingress +---------+
| internet | ---------> | Service |
+----------+ +---------+
Ingress Controller 負責實現入口(即ingress的意思),通常使用負載均衡器,但它也可以配置邊緣路由器或其他前端以幫助處理流量。Ingress不會隨意暴露端口或協議,向Internet公開HTTP和HTTPS以外的服務通常使用Service.Type=NodePort
或Service.Type=LoadBalancer
類型的服務。
和其他K8S資源一樣,Ingress必須要有一個 Ingress Controller,僅有 Ingress Resource 是無效的,Ingress也是無法正常工作的。Ingress Controller不會隨着集羣的啓動而自動啓動,且Ingress Controller有多種,目前K8S只支持和維護GCE(Google Kubernetes Engine)和nginx2種Controller。
【Ingress Resource】
Ingress Resource 則和大部分的K8S資源差不多,需要apiVersion
、kind
和metadata
等字段,下面一個最小ingress resource的示例:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
backend:
serviceName: test
servicePort: 80
Ingress通常都是使用註解annotations
來配置某些選項,具體可配置選項、支持哪些註解取決於Ingress Controller類型,上述是一個rewrite-target
的註解,spec
字段包含了負載均衡器或代理服務器的配置,最重要的是它包含了所有傳入請求的匹配規則。注:Ingress Resource僅支持HTTP通信的規則。
Ingress rules是Ingress中比較重要的一部分,即上述示例中的rules
字段的含義(它是一個可以包含多種rule
對象的數組),每個Http的rule
都應該包含如下的信息:
host
:可選參數,用於限定rule適用的host,如指定爲foo.bar.com
,那該rule只應用於這個host,上述示例中未指定,則該rule
適用於通過指定IP地址的所有入站HTTP流量;-paths
:一系列的path對象組成的數組,上述示例中只有一個path爲/testpath
,每個path都有一個backend
對象(代表的是後端服務)與之對應;backend
:是一個serviceName
和servicePort
的組合對象,只有host
和path
都匹配成功的情況下,LoadBalancer纔會將流量導向引用的服務。
通常Ingress會有一個 Default Backend,當在上述我們自定義的Ingress Resource沒有匹配的rule
對象時,流量就會被導向這個Default Backend。當然這個default backend需要我們自己去配,通常不會在Ingress Resource中配置default backend,而是應該在Ingress Ctontroller中進行指定。
【Ingress的類型】
1.Single Service Ingress
即單服務Ingress,K8S當前是支持暴露單個服務的,可以通過在沒有規則的情況下指定默認後端來使用ingress來完成此操作,如:
# ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: testsvc
servicePort: 80
注:
安裝完ingress後,還要執行:
# 創建命名空間 ingress-nginx
kubectl create namespace ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/with-rbac.yaml
kubectl apply -f ingress.yaml
即可,通過kubectl get ingress test-ingress
可以查看詳情,包括Ingress分配給這個default backend的IP。
2.Simple fanout
fanout基於請求的HTTP URI將配置流量從單個IP路由到多個服務,Ingress 是允許將負載平衡器的數量降到最低,如:
foo.bar.com -> 178.91.123.132 -> / foo service1:4200
/ bar service2:8080
上述的過程需要一個如下的Ingress(同一個域名配置了2個path):
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 4200
- path: /bar
backend:
serviceName: service2
servicePort: 8080
3.基於名字虛擬host
和2類似,這種類型的Ingress支持將HTTP流量路由到在同一個IP上的多個host name,流程如下:
foo.bar.com --| |-> foo.bar.com s1:80
| 178.91.123.132 |
bar.foo.com --| |-> bar.foo.com s2:80
此時的Ingress Resource應該爲:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80
此時Ingress會告訴負載均衡器將請求根據Host header路由到指定地方。如果不指定host,任意到Ingress Controller IP地址的Web流量都可以匹配,而不需要基於名稱的虛擬主機。
4.TLS
可以指定包含一對TLS私鑰、證書的方式來保護Ingress,當前Ingress只支持TLS 443端口和TLS終端,TLS必須包含tls.crt
和tls.key
,如:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls
在Ingress中引用這個密鑰將告訴Ingress Controller使用TLS保護Ingress從客戶端到負載平衡器的通道。此外,還需要確保創建的TLS機密來自包含CN的證書,如:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- sslexample.foo.com
secretName: testsecret-tls
rules:
- host: sslexample.foo.com
http:
paths:
- path: /
backend:
serviceName: service1
servicePort: 80
注:TLS對於nginx和GCE Controller稍有不同
【Update Ingress】
更新一個已有的Ingress,可以使用如下的流程:
# 查看已有配置
kubectl describe ingress test
# 修改配置
kubectl edit ingress test
保存後將會更新資源,它將告訴Ingress Controller重新配置負載均衡器,當然也可以使用kubectl replace -f xx.yaml
來重新載入配置即可。
注:
ingress的文檔比較好用,貼一下Annotations,這裏有個坑,如果使用rancher進行配置,那所有字段儘量不要再用''
或""
包括,rancher的解析做的不是很好,他會將引號作爲內容的一部分設置爲對應的註解值,很坑!
附:常用ingress配置
常用的ingress配置模板如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ .Values.deploy.app }}
annotations:
{{- range $key, $value := .Values.service.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
labels:
service-cluster: {{ .Values.deploy.serviceCluster }}
app: {{ .Values.deploy.app }}
project-type: java
spec:
rules:
# 可以有多個
- host: {{ .hosts }}
http:
paths:
# 可以有多個(可以正則)
- path: {{ .path }}
backend:
serviceName: {{ .serviceName }}
servicePort: {{ .servicePort }}
# 可以有多個
tls:
# 可以有多個
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
1. 跨域(單個或多個)
在配置跨域時,如果使用rancher可視化面板配置nginx.ingress.kubernetes.io/enable-cors
和nginx.ingress.kubernetes.io/cors-allow-origin
註解時千萬不能加引號,不然找半天找不到錯誤在哪。但寫Chart的yaml配置文件時,該怎麼寫就怎麼寫,string
類型的還是需要加上引號,跨域的示例配置爲:
# 方式1:直接使用註解(但此時只能配置一個域跨域)
annotations:
# 使用rewrite配置contextPath
nginx.ingress.kubernetes.io/rewrite-target: /account/$1
# 配置跨域
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "http://k8s-www.hhu.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
# 方式2:使用自定代碼段註解,自定寫跨域(這種方式可以配置多個域名跨域)
nginx.ingress.kubernetes.io/server-snippet: |
# 配置跨域(多個,單個可用註解)
set $corsHost '';
set $httpHost '';
set $httpHeaders 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-Type,X-Forwarded-For,xfilecategory,xfilename,xfilesize';
if ( $http_origin ~ http://k8s-www.hhu.com){
set $corsHost $http_origin;
set $httpHost 'k8s-www.hhu.com';
}
if ( $http_origin ~ http://k8s-m.hhu.com){
set $corsHost $http_origin;
set $httpHost 'k8s-m.hhu.com';
}
add_header Access-Control-Allow-Origin $corsHost always;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header Access-Control-Allow-Headers $httpHeaders;
add_header Access-Control-Allow-Credentials 'true';
比較推崇的是上述第二種的配置方式,注意,在添加Access-Control-Allow-Origin
的header時必須接上always
,否則不生效。
2. ingress中的rewrite
ingress中配置rewrite有一個自己的註解nginx.ingress.kubernetes.io/rewrite-target
,可以自己配置爲指定值,我工作中更習慣將其配置爲各個服務自己的contextPath,比如:
metadata:
annotations:
...
nginx.ingress.kubernetes.io/rewrite-target: /account/$1
spec:
rules:
# 可以有多個
- host: www.hhu.com
http:
paths:
# 可以有多個(可以正則)
- path: /($/.*)
backend:
serviceName: server-account
servicePort: 80
上述path
使用的是一個正則/($/.*)
,可以匹配/
或者任意以/
開頭任意多個字符,我比較推薦使用這個正則/($/.*)
,而不推薦使用/(.*)
,因爲.*
要求你至少匹配1個字符,即匹配不到/
的path。可以配置www.hhu.com
域名下任意路徑,在其前面加上/account
(比如訪問的是www.hhu.com/info
會被路由到www.hhu.com/account/info
)。
ingress中的rewrite同樣可以使用nginx.ingress.kubernetes.io/server-snippet
註解來更加多樣化的配置,示例如下:
nginx.ingress.kubernetes.io/server-snippet: |
# analyzer
rewrite /behavior/data($|.*) /analyzer/behavior/data$1 break;
rewrite /hhu/event($|.*) /analyzer/hhu/event$1 break;
# mine 或者寫成 rewrite /private(/|$)(.*) /mine/private$1$2 break;
rewrite /private($|.*) /mine/private$1 break;
rewrite /mine($|.*) /mine$1 break;
# shop
rewrite /shops($|.*) /shops$1 break;
# task
rewrite /internal($|.*) /task/internal$1 break;
# portal
rewrite /($|.*) /portal/$1 break;
上述注意,break
千萬不能丟,像類似於/($|.*)
全匹配的正則一定要寫到最下面,否則它會最先匹配(原因未知,不是說好的路徑最長匹配嗎……)。