生产环境下,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 8000/TCP 21d
kubernetes-dashboard LoadBalancer 10.254.253.226 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,资源路径不以./或../或/开头 或 ,资源路径以./开头 |
1.匹配/path时,只能通过https://xxxx/path/ 访问2.匹配/path,并且后端重定向到sub/,这时通过 https://xxxx/path/ 或https://xxxx/path 都可以访问 |
,资源路径不以./或../或/开头 |
只能匹配/,其他路径匹配都无法正常工作 确实碰到这种情况,例如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/
都可以成功登陆。
欢迎访问 钟潘的博客