使用traefik反向代理k8s dashboard

zhongpan

生产环境下,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,将其中的关键点记录于此。

<!--more-->

安装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

详细的配置方法见官方文档,上述关键点如下:

  1. 开启https入口,设置ssl.enabled=true,然后提供证书和key,上述通过从node节点本地目录mount到pod的方式,所以每个node节点要先放好证书和key,更好的方式是通过k8s secret,创建secret然后mount到pod
  2. 如何访问到入口,我是通过在node上打开端口,这时通过pod所在node就可以访问到入口,通过http://nodeiphttps://nodeip;还可以使用NodePort类型service,这样通过http://any-nodeip:http-nodeporthttps://any-nodeip:https-nodeport访问,value设置 serviceType: NodePort
  3. 路由匹配规则我使用的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的路由会出现以下几种情形:

  1. http->http
  2. http->https
  3. https->http
  4. 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错误。

image-20191129064738766

一开始以为是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登陆界面:

image-20191129063718980

一开始我是使用的http入口,使用Token方式登陆,没有任何响应,通过开发者工具查看,发现问题是在使用http入口时,header中没有携带jweToken,导致认证失败,必须使用https入口。

回想起之前通过kubectl proxy,即http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/也是登陆不了,其实是一样的问题。

使用http入口登陆失败:

image-20191129064020553

使用https入口时,jweToken是携带了,登陆成功:

image-20191129064137163 )

所以果断设置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/都可以成功登陆。

欢迎访问钟潘的博客
阅读 1.9k

13年研发经验

8 声望
1 粉丝
0 条评论

13年研发经验

8 声望
1 粉丝
宣传栏