Kubernetes生态Ingress组件Traefik v2.0浅析

前言

上一篇文章简单介绍了下Kubernetes生态的几个组件,这篇文章重点讲解下其中的Traefik组件,Traefik组件类似与Nginx,可以为整个集群做服务暴露、域名控制等等的作用,目前Traefik主要分为两个版本,v1.x与v2.x,这两个版本之间差距较大,让人感觉在使用不同的软件。本篇文章是以v2.x版本为基础来演示的,相关脚本代码都在Github仓库https://github.com/lateautumn4lin/KubernetesResearch里面,大家使用的时候可以切换目录到ClusterEcology/initTraefik下面。

Traefik v2.x升级升了哪些点

我们先来看看Traefik v2.x相对于v1.x版本有哪些新的特性以及升级了哪些旧特性。

1. 支持 SNI 路由和多协议端口的 TCP


Traefik v2.x第一个值得关注的功能就是支持SNI路由和多协议端口的 TCP,这样我们就可以在配置路由的时候指定Service的名称即可,针对与v1.x 版本只支持http(s)协议的路由,v2.x版本增加了对于TCP协议的支持,这样我们还可以把某些数据库的服务暴露出来。

下面是一个简单的示例配置 - 使用最新支持的 YAML 文件格式,将请求路由到一个数据库上面去:

tcp:
  routers:
    to-database:
      entrypoints:
      - database-entrypoint
      rule: HostSNI(`*`)
      service: database-service
  services:     
    database-service:
      loadBalancer:
        servers:
        - address: xx.xx.xx.xx:xx

上面这个配置示例表示每个以 database-entrypoint 结尾的请求都将被路由到 database-service 这个服务上去。

此外通过TLS,Traefik 还可以根据 SNI 来路由 TCP 请求。在下面示例中,Traefik 就将根据 SNI 将请求路由到两个数据库:

tcp:
  routers:
    to-db-1:
      entrypoints:
      - web-secure
      rule: "HostSNI(`db1.domain`)"
      service: "db1"
      tls: {} 
    to-db-2:
      entrypoints:
      - web-secure
      rule: "HostSNI(`db2.domain`)"
      service: "db2"
      tls: {}

另外 Traefik 还是支持 HTTP 和 TCP 在同一个端口上,如果你希望获得相同的入口的同时获取 HTTP 和 TCP 请求,那么 Traefik 可以很完美的来处理它。

tcp:
  routers:
    to-db-1:
      entrypoints:
      - web-secure
      rule: "HostSNI(`db1.domain`)"
      service: "db-1"
      tls: {}
http:
  routers:
    to-db1-dashboard:
      entrypoints:
      - web-secure
      rule: "Host(`dashboard.db1.domain`)"
      service: "db1-dashboard"
      tls: {}

比如上面这个示例中,dashboard.db1.domain 上的 HTTP 请求将路由到数据库的 Dashboard 服务上,而上面的 db1.domain 上的 TCP 请求将路由到数据库上面去。

2. 使用中间件完全自定义路由


在 Traefik 2.0 中还引入了中间件功能,可以用于将请求路由到目的地之前或之后来调整请求,相比于之前的单纯做服务暴露的功能,新版本无论是在软件架构设计和长远发展来看,都更偏向于Nginx的生态设计。

首先我们可以声明一个中间件,在任意数量的路由上面都可以重用它们。下面我们来演示下如何配置中间件,声明一个 BasicAuth 中间件来控制对我们服务的访问(这次使用 TOML 来配置):

# 为两个用户声明一个 basicauth 的中间件
[http.middlewares.test-auth.basicAuth]
  users = ["user1:hashed", "user2:hashed"]
# 将上面的中间件应用到路由上面去
[http.routers.my-router.to-service]
  rule = "host(`my-protected.domain`)"
  middlewares = ["test-auth"]
  service = "service1"

此外可以声明一个链来组合绑定这些中间件,并反复使用它们,对于 Kubernetes 用户来说,还可以使用 Traefik 的新 CRD 来进行更加清晰明了的配置,而不需要复杂的注解。(可以在文档中找到有关 IngressRoute 对象的更多信息。)如下所示:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: test
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`mydomain`)
      kind: Rule
      services:
        - name: whoami
          port: 80
      middlewares:
        - name: secured
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: secured
spec:
  chain:
    middlewares:
    - name: https-only
    - name: known-ips
    - name: auth-users
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: auth-users
spec:
  basicAuth:
    secret: secretUsers # 兼容 K8S secrets 对象
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: https-only
spec:
  redirectScheme:
    scheme: https
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: known-ips
spec:
  ipWhiteList:
    sourceRange:
    - 192.168.1.7
    - 127.0.0.1/32

上面示例中 secured 这个中间件就是有 https-only、know-ips、auth-users 这3个中间件组成的一个链式中间件。

而且在 Traefik 中内置了许多中间件:路径操作、多种身份验证机制、缓冲、断路器、重试、压缩、错误处理、headers、IP 白名单、限速、重定向等。此外官方还重新设计了代码架构,可以让开发者更加容易提供第三方的中间件。

3. 全新的 Dashboard

新版本设计了全新的 WebUI,目标是向用户一目了然地展示集群上信息,还希望显示可以启用的哪些功能特性。界面上的巨大差异也是让人感觉使用了不同的两款软件。

除了上面的几个大的特性之外,Traefik v2.0还支持了类似于金丝雀发布,流量控制的功能,感觉上算是集成了部分Service Mesh(微服务网关)的功能,更多的特性就不多介绍了,可以去看看官网文档来详细了解https://docs.traefik.io/v2.0/

Traefik v2.x安装实践

关于Traefik v2.x安装实践主要分为两个大部分,第一部分是安装Traefik v2.x服务,第二部分是配置好对应的服务路由。

1. Traefik v2.x服务安装

1.1 创建 CRD 资源

在 traefik v2.0 版本后,开始使用 CRD(Custom Resource Definition)来完成路由配置等,所以需要提前创建 CRD 资源。

查看具体的代码

## IngressRoute
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
---
## IngressRouteTCP
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
---
## Middleware
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption

创建 Traefik CRD 资源

kubectl apply -f traefik-crd.yaml
1.2 创建 RBAC 权限

Kubernetes 在 1.6 版本中引入了基于角色的访问控制(RBAC)策略,方便对 Kubernetes 资源和 API 进行细粒度控制。Traefik 需要一定的权限,所以这里提前创建好 Traefik ServiceAccount 并分配一定的权限。

查看具体的代码

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: kube-system
  name: traefik-ingress-controller
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
rules:
  - apiGroups: [""]
    resources: ["services","endpoints","secrets"]
    verbs: ["get","list","watch"]
  - apiGroups: ["extensions"]
    resources: ["ingresses"]
    verbs: ["get","list","watch"]
  - apiGroups: ["extensions"]
    resources: ["ingresses/status"]
    verbs: ["update"]
  - apiGroups: ["traefik.containo.us"]
    resources: ["middlewares"]
    verbs: ["get","list","watch"]
  - apiGroups: ["traefik.containo.us"]
    resources: ["ingressroutes"]
    verbs: ["get","list","watch"]
  - apiGroups: ["traefik.containo.us"]
    resources: ["ingressroutetcps"]
    verbs: ["get","list","watch"]
  - apiGroups: ["traefik.containo.us"]
    resources: ["tlsoptions"]
    verbs: ["get","list","watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: kube-system

创建 Traefik RBAC 资源

kubectl apply -f traefik-rbac.yaml -n kube-system
1.3 创建 Traefik 配置文件

由于 Traefik 配置很多,通过 CLI 定义不是很方便,一般时候选择将其配置选项放到配置文件中,然后存入 ConfigMap,将其挂入 traefik 中。

查看具体的代码

kind: ConfigMap
apiVersion: v1
metadata:
  name: traefik-config
data:
  traefik.yaml: |-
    serversTransport:
      insecureSkipVerify: true
    api:
      insecure: true
      dashboard: true
      debug: true
    metrics:
      prometheus: ""
    entryPoints:
      web:
        address: ":80"
      websecure:
        address: ":443"
    providers:
      kubernetesCRD: ""
    log:
      filePath: ""
      level: error
      format: json
    accessLog:
      filePath: ""
      format: json
      bufferingSize: 0
      filters:
        retryAttempts: true
        minDuration: 20
      fields:
        defaultMode: keep
        names:
          ClientUsername: drop
        headers:
          defaultMode: keep
          names:
            User-Agent: redact
            Authorization: drop
            Content-Type: keep

创建 Traefik ConfigMap 资源

kubectl apply -f traefik-config.yaml -n kube-system
1.4 节点设置 Label 标签

由于是 Kubernetes DeamonSet 这种方式部署 Traefik,所以需要提前给节点设置 Label,这样当程序部署时 Pod 会自动调度到设置 Label 的节点上。

节点设置 Label 标签

格式:kubectl label nodes [节点名] [key=value]

kubectl label nodes k8s-node-2-12 IngressProxy=true

查看节点是否设置 Label 成功

[root@master1 initTraefik]# kubectl get nodes --show-labels
NAME      STATUS   ROLES    AGE   VERSION   LABELS
master1   Ready    master   26h   v1.17.2   IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,edgenode=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-master,kubernetes.io/os=linux,node-role.kubernetes.io/master=

如果想删除标签,可以使用 kubectl label nodes k8s-node-2-12 IngressProxy- 命令

1.5 Kubernetes 部署 Traefik

这里还是按以前部署 Traefik 1.7 一样,用 DaemonSet 方式部署,便于在多服务器间扩展,用 hostport 方式占用服务器 80、443 端口,方便流量进入。

查看具体的代码

apiVersion: v1
kind: Service
metadata:
  name: traefik
spec:
  ports:
    - name: web
      port: 80
    - name: websecure
      port: 443
    - name: admin
      port: 8080
  selector:
    app: traefik
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: traefik-ingress-controller
  labels:
    app: traefik
spec:
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      name: traefik
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 1
      containers:
        - image: traefik:v2.0.5
          name: traefik-ingress-lb
          ports:
            - name: web
              containerPort: 80
              hostPort: 80           #hostPort方式,将端口暴露到集群节点
            - name: websecure
              containerPort: 443
              hostPort: 443          #hostPort方式,将端口暴露到集群节点
            - name: admin
              containerPort: 8080
          resources:
            limits:
              cpu: 2000m
              memory: 1024Mi
            requests:
              cpu: 1000m
              memory: 1024Mi
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
          args:
            - --configfile=/config/traefik.yaml
          volumeMounts:
            - mountPath: "/config"
              name: "config"
      volumes:
        - name: config
          configMap:
            name: traefik-config 
      tolerations:              #设置容忍所有污点,防止节点被设置污点
        - operator: "Exists"
      nodeSelector:             #设置node筛选器,在特定label的节点上启动
        IngressProxy: "true"

Kubernetes 部署 Traefik

kubectl apply -f traefik-deploy.yaml -n kube-system

到此 Traefik v2.0 应用已经部署完成。

2. 服务路由配置

2.1 配置 HTTP 路由规则 (Traefik Dashboard 为例)

Traefik 应用已经部署完成,但是想让外部访问 Kubernetes 内部服务,还需要配置路由规则,这里开启了 Traefik Dashboard 配置,所以首先配置 Traefik Dashboard 看板的路由规则,使外部能够访问 Traefik Dashboard。

查看具体的代码

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard-route
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`traefik.lateautumn4lin.dashboard`)
      kind: Rule
      services:
        - name: traefik
          port: 8080

创建 Traefik Dashboard 路由规则对象

kubectl apply -f traefik-dashboard-route.yaml -n kube-system

接下来配置 Hosts,客户端想通过域名访问服务,必须要进行 DNS 解析,由于这里没有 DNS 服务器进行域名解析,所以修改 hosts 文件将 Traefik 指定节点的 IP 和自定义 host 绑定。打开电脑的 Hosts 配置文件,往其加入下面配置:

192.144.152.23  traefik.lateautumn4lin.dashboard

配置完成后,打开浏览器输入地址:http://traefik.lateautumn4lin.dashboard 打开 Traefik Dashboard。

2.2 配置 HTTPS 路由规则(Kubernetes Dashboard 为例)

这里我们创建 Kubernetes 的 Dashboard 看板,它是 Https 协议方式,由于它是需要使用 Https 请求,所以我们配置基于 Https 的路由规则并指定证书。

创建证书文件

# 创建自签名证书
$ openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=cloud.lateautumn4lin.dashboard"

# 将证书存储到 Kubernetes Secret 中
$ kubectl create secret generic cloud-mydlq-tls --from-file=tls.crt --from-file=tls.key -n kube-system

查看具体的代码

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: kubernetes-dashboard-route
spec:
  entryPoints:
    - websecure
  tls:
    secretName: cloud-mydlq-tls
  routes:
    - match: Host(`cloud.lateautumn4lin.dashboard`) 
      kind: Rule
      services:
        - name: kubernetes-dashboard
          port: 443

创建 Kubernetes Dashboard 路由规则对象

kubectl apply -f kubernetes-dashboard-route.yaml -n kube-system

跟上面一样,配置 Hosts 文件

192.144.152.23  cloud.lateautumn4lin.dashboard

配置完成后,打开浏览器输入地址:https://cloud.lateautumn4lin.dashboard/ 打开 Dashboard,和我们上篇文章看到的效果相同,表示Traefik已经代理好HTTPS的请求了。

2.3 配置 TCP 路由规则(Redis 为例)

为了演示方便,我们这里只部署单节点的 Redis,对于 Redis 集群模式并不是我们这里的重点,下面是我们部署使用的资源清单文件:(redis.yaml)

查看具体的代码

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:3.2.11
        ports:
        - containerPort: 6379
          protocol: TCP

---

apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis

创建 Redis服务

kubectl apply -f redis.yaml

暴露 TCP 服务
由于 Traefik 中使用 TCP 路由配置需要 SNI,而 SNI 又是依赖 TLS 的,所以我们需要配置证书才行,但是如果没有证书的话,我们可以使用通配符 * 进行配置,我们这里创建一个 IngressRouteTCP 类型的 CRD 对象(前面我们就已经安装了对应的 CRD 资源):(ingressroute-redis.yaml)

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
  name: redis
spec:
  entryPoints:
    - redis
  routes:
  - match: HostSNI(`*`)
    services:
    - name: redis
      port: 6379

创建上面的 IngressRouteTCP 对象

kubectl apply -f ingressroute-redis.yaml 

创建完成后,同样我们可以去 Traefik 的 Dashboard 页面上查看是否生效:

如图可见,虽然路由导出成功,但是却提示错误,这是因为我们要把Redis 服务和IngressRouteTCP 对象部署在相同的namespace下面它们才会彼此可见,所以我们在apply创建服务的同时,可以使用-n指定namespace。

然后我们配置一个域名解析到 Traefik 所在的节点,然后通过 6379 端口来连接 Redis 服务:

$ redis-cli -h redis.lateautumn4lin.com -p 6379
redis.lateautumn4lin.com:6379> ping
PONG
redis.lateautumn4lin.com:6379> set hello world
OK
redis.lateautumn4lin.com:6379> get hello
"world"
redis.lateautumn4lin.com:6379>

到此 Traefik v2.0 服务路由配置已经部署完成。

更多Traefik v2.x生产实战

目前市面上的实战较少,主要还是关于Traefik v2.x的某些特性的测试案例,之后有待补充。


文章作者: Lateautumn4lin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Lateautumn4lin !
评论
评论
 上一篇
kubernetesv1.17集群生态搭建笔记 kubernetesv1.17集群生态搭建笔记
前言上一次接触到kubernetes集群的时候已经是一年以前了,那个时候官方的版本还只是v1.10,而现在过去一年的时间了,官方版本已经快速的迭代到了v1.17了,社区也越来越成熟、相关的生态组件也越来越丰富,可见在过去的K8S元年,它发
下一篇 
为了让大家能够看到K8S Dashboard DEMO,我创建了一个“只读用户” 为了让大家能够看到K8S Dashboard DEMO,我创建了一个“只读用户”
前言这两天抽空搞了下Kubernetes集群和Traefik,有很多朋友私信我想要看看实际的Kubernetes集群 Dashboard Demo效果,所以简单的把这两个服务开放出来啦! PS:相关代码都在Github仓库 https:/
  目录