生產環境下,k8s集群對外暴露服務主要有LoadBalancer和Ingress兩種方式:
- LoadBalancer:需要雲廠商支持,使用k8s service的負載均衡能力,也就是依靠iptables/ipvs的能力,可用於各種協議
- Ingress:相對更加靈活,通過反向代理服務器實現負載均衡,僅用於http/https協議,這種場景下需要額外的反向代理服務以及ingress controller,nginx是大家熟知的反向代理,在k8s時代,出現了nginx-ingress,就是nginx+ingress controller的組合,ingress controller負責根據ingress資源生成nginx配置,當配置有變化是重啟nginx。同時也出現了雲原生的反向代理traefik,它相當於把ingress controller包含到其中合為一體,並且能夠動態感知路由規則變化,不需重啟。
traefik是一個相對較新的反向代理,網上相關資料不是特別豐富,研究了好幾天,才成功訪問到k8s dashboard,將其中的關鍵點記錄於此。
安裝traefik
使用helm安裝,最新chart使用的traefik 1.7.19:
helm install stable/traefik -f traefik-values.yaml
traefik-values.yaml:
rbac:
enabled: true
dashboard:
enabled: true # 啟用traefik dashboard
ingress:
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
deployment:
hostPort:
httpEnabled: true # traefik pod所在node上開啟80端口
httpsEnabled: true # traefik pod所在node上開啟443端口
dashboardEnabled: true # traefik pod所在node上開啟8080端口,共traefik dashboard使用
ssl:
insecureSkipVerify: true # frontend不驗證https的benkend
enabled: true # 啟用https入口
extraVolumes:
- name: traefik-ssl
hostPath:
path: /share/k8s/traefik/ssl # 其中存放https入口的證書和key,名字必須為tls.crt,tls.key
type: DirectoryOrCreate
extraVolumeMounts:
- name: traefik-ssl
mountPath: /ssl # traefik pod從/ssl目錄讀取上述tls.crt,tls.key
詳細的配置方法見官方文檔,上述關鍵點如下:
- 開啟https入口,設置ssl.enabled=true,然后提供證書和key,上述通過從node節點本地目錄mount到pod的方式,所以每個node節點要先放好證書和key,更好的方式是通過k8s secret,創建secret然后mount到pod
- 如何訪問到入口,我是通過在node上打開端口,這時通過pod所在node就可以訪問到入口,通過http://nodeip或https://nodeip;還可以使用NodePort類型service,這樣通過http://any-nodeip:http-nodeport或https://any-nodeip:https-nodeport訪問,value設置 serviceType: NodePort
- 路由匹配規則我使用的PathPrefixStrip,默認是host名匹配
因為啟用了traefik dashboard,安裝traefik會自動創建dashboard的ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
labels:
app: traefik
chart: traefik-1.82.1
heritage: Tiller
release: traefik
name: traefik-dashboard
namespace: default
spec:
rules:
- host: traefik.example.com
http:
paths:
- backend:
serviceName: traefik-dashboard
servicePort: dashboard-http
traefik是通過標簽app: traefik選擇到需要感知的ingress。自己添加的ingress注意包含這個標簽。上述annotations和host是從value而來。因為我不想配host,所以用PathPrefixStrip路由規則,我修改了上述ingress如下:
spec:
rules:
- http:
paths:
- backend:
serviceName: traefik-dashboard
servicePort: dashboard-http
path: /traefik
這樣當使用http://nodeip/traefik就可以訪問到dashboard,因為在node上也開啟了dashboard端口,也可以通過http://nodeip:8080訪問。
代理k8s dashboard
目前最新的k8s dashboard(v2.0.0-beta6)安裝在kubernetes-dashboard namespace:
kubectl get svc -n kubernetes-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard-metrics-scraper ClusterIP 10.254.238.13 <none> 8000/TCP 21d
kubernetes-dashboard LoadBalancer 10.254.253.226 <pending> 443:30223/TCP 21d
增加ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
labels:
app: traefik
name: kubernetes-dashboard
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 443
path: /k8s
代理https后端
k8s dashboard只支持https訪問,首先卡住的問題是如何代理https服務,frontend到backend的路由會出現以下幾種情形:
-
http->http
-
http->https
-
https->http
-
https->https
當backend為https時,無論frontend是http或https,也就是2和4,都會報500錯誤,因為frontend無法驗證backend,此時解決方法:
- 要么設置insecureSkipVerify,這樣比較簡單,如果采用這種方式frontend最好總是采用https,也就是設置redirect
- 要么設置ingress tls,配置host的tls證書信息
我采用的設置insecureSkipVerify的方法。一般最佳的使用方式也是入口總是用https,然后終結tls,后端是否https不重要。
代理不同namespace服務
解決上述問題后,接下來遇到k8s dashboard服務無法訪問問題,在traefik dashboard中顯示為紅色,原因是helm安裝traefik默認在default namespace中,而k8s dashboard安裝在kubernetes-dashboard namespace中,不能跨namespace訪問到服務,解決方法:
-
要么將traefik安裝到和k8s dashboard同一空間
-
要么通過ExternalName將dashboard service引入到default namespace
apiVersion: v1 kind: Service metadata: name: kubernetes-dashboard namespace: default spec: ports: - name: https port: 443 protocol: TCP targetPort: 443 sessionAffinity: None type: ExternalName externalName: kubernetes-dashboard.kubernetes-dashboard.svc.cluster.local
我采用的ExternalName方法。service的完整域名是servicename.namespace.svc.cluster.local,cluster.local是kubelet中配置的。
基於path路由
服務可以訪問了,但是又出現了MIME type is not a supported stylesheet MIME type錯誤。

一開始以為是traefik在reponse header中加入了 X-Content-Type-Options: nosniff,但是發現traefik默認是不加入的。
最后發現是url路徑問題,我的ingress僅使用path路由,沒有使用host。
當使用https://nodeip/k8s訪問k8s dashboard時,因為路由規則是PathPrefixStrip,到后端的請求是https://nodeip,這時得到主頁,文件名是k8s,主頁面k8s中的css,js等文件路徑是相對於當前文檔路徑的,所以request url是https://nodeip/xxx.css,這時就匹配不上路由規則,出現上述錯誤。
如果使用https://nodeip/k8s/訪問dashboard,就一切正常了。
所以使用路徑匹配路由時是存在一定風險的,和主頁中的資源路徑定義有關:
| 主頁中css,js等資源路徑定義方式 | 說明 |
|---|---|
| 沒有定義base,資源路徑不以./或../或/開頭 或 <base href="./">,資源路徑以./開頭 |
1.匹配/path時,只能通過https://xxxx/path/訪問 2.匹配/path,並且后端重定向到sub/,這時通過https://xxxx/path/或https://xxxx/path都可以訪問 |
<base href="/">,資源路徑不以./或../或/開頭 |
只能匹配/,其他路徑匹配都無法正常工作 確實碰到這種情況,例如monocular |
所以最好的方式還是通過host匹配路由。
dashboard認證
了解上述問題后,終於進入到dashboard登陸界面:

一開始我是使用的http入口,使用Token方式登陸,沒有任何響應,通過開發者工具查看,發現問題是在使用http入口時,header中沒有攜帶jweToken,導致認證失敗,必須使用https入口。
回想起之前通過kubectl proxy,即http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/也是登陸不了,其實是一樣的問題。
使用http入口登陸失敗:

使用https入口時,jweToken是攜帶了,登陸成功:
)
所以果斷設置frontend總是https,values增加traefik.ingress.kubernetes.io/redirect-entry-point: https,然后helm upgrade,自己增加的ingress需要自己修改:
dashboard:
enabled: true
ingress:
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
traefik.ingress.kubernetes.io/redirect-entry-point: https
# 不要使用ingress.kubernetes.io/ssl-redirect: "true",因為會丟掉path
這樣無論使用http://nodeip/k8s/還是https://nodeip/k8s/都可以成功登陸。
歡迎訪問鍾潘的博客
