1. 基础简介
Traefik 是一个为了让部署微服务更加便捷而诞生的现代 HTTP 反向代理、负载均衡工具。 它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, File...) 来自动化、动态的应用它的配置文件设置。
2. 核心组件
组件 | 作用 |
---|---|
Providers | 用来自动发现平台上的服务,可以是编排工具、容器引擎云提供商或者键值存储。Traefik 通过查询 Providers 的 API 来查询路由的相关信息,一旦检测到变化,就会动态的更新路由 |
Entrypoints | 监听传入的流量,是网络的入口点,定义了接受请求的端口 (HTTP 或者 TCP) |
Routers | 分析请求 (Host, Path, Headers, SSL 等),负责将传入的请求连接到可以处理这些请求的服务上去 |
Middlewares | 中间件,用来修改请求或者根据请求来做出判断,中间件被附件到路由上,是一种在请求发送到服务之前调整请求的一种方法 |
Service | 中间件,用来修改请求或者根据请求来做出判断,中间件被附件到路由上,是一种在请求发送到服务之前调整请求的一种方法 |
当请求 Traefik 时,请求首先到Entrypoints
,然后分析传入的请求,查看他们是否与定义的Routers
匹配。如果匹配,则会通过一系列Middlewares
处理,再到 traefik 的Services
上做流量转发,最后请求到 kubernetes 的 services 上。
3. Traefik 对比 Nginx
Traefik 组件类比 Nginx 概念:
组件名称 | 功能 | Nginx 相同概念 |
---|---|---|
Providers | 监听路由信息变化,更新路由 | 修改 nginx 配置,reload 服务 |
Entrypoints | 网络入口,监听传入的流量 | 配置文件 listen 指定监听端口 |
Routers | 分析传入的请求,匹配规则 | 配置文件 server_name + location |
Middlewares | 中间件,修改请求或响应 | location 配置段中添加的缓存、压缩、请求头等配置 |
Service | 请求转发 | http 配置段中的 upstream |
Kubernetes 中 Traefik 与 Nginx-Ingress 对比:
由于微服务架构以及 Docker 和 Kubernetes 编排工具最近几年才开始流行,所以一开始的反向代理服务器如 Nginx、Apache 并未提供支持,才会出现 Ingress Controller 这种东西来做 Kubernetes 和前端负载均衡器如 Nginx 之间做衔接。 Ingress Controller 的存在就是为了能跟 Kubernetes 交互,然后写入 Nginx 配置,最后 Reload。
- Nginx-Ingress: 使用 Nginx 作为前端负载均衡,通过 Ingress Controller 不断的和 Kubernetes Api 交互,实时获取后端 Service,Pod 等的变化,然后动态更新 Nginx 配置,并刷新使配置生效,达到服务发现的目的。
- Traefik-Ingress: Traefik 本身设计的就能够实时跟 Kubernetes Api 交互,感知后端 Service,Pod 等的变化,自动更新配置并重载。
Nginx Ingress | Traefik ingress | |
---|---|---|
协议 | Http / Https、Http2、Grpc、Tcp / Udp | Http / Https、Http2、Grpc、Tcp、Tcp + Tls |
路由匹配 | Host、Path | Host、Path、Headers、Query、Path Prefix、Method |
命名空间支持 | - | 共用或指定命名空间 |
部署策略 | - | 金丝雀部署、蓝绿部署、灰度部署 |
upstream 探测 | 重试、超时、心跳探测 | 重试、超时、心跳探测、熔断 |
负载均衡算法 | 轮询、会话保持、最小连接、最短时间、一致性Hash | 加权轮询、动态轮询、会话保持 |
优点 | 简单易用,易接入 | Golang 编写,部署容易,支持众多的后端,内置 WebUI |
缺点 | 没有解决 Nginx Reload,插件多但是扩展性能差 | 性能略逊于 Nginx,但强于 HAProxy |
Traefik 有自己的 Web UI,一般安装完成后通过 8080 端口访问:
4. Traefik 的各种 Providers 配置发现
Traefik 中的配置发现是通过下面的一些 providers 来实现的。providers 是现有的一些基础架构组件,可以是编排工具,容器引擎,云提供商或者 key-value 存储都可以。
Traefik 通过查询 providers 的 API 来查找有关路由的相关信息,Traefik 每次检测到更改时,都会动态更新路由。
编排器:
虽然每个 provider 都是不同的,但是我们还是可以将这些 provider 大致分为4组:
- 基于标签(每个部署的容器都附件了一组标签)
- 基于键值(每个部署的容器使用相关信息来更新 key-value 存储)
- 基于注解(带有注解的单独对象来定义容器的一些特性)
- 基于文件(一些旧的配置文件)
支持的 Providers:
下面列出的是现在 Traefik 支持的一些 providers:
Provider | 类型 | 配置类型 |
---|---|---|
File | 手动 | TOML/YAML 文件 |
Docker | 编排器 | 标签 |
Kubernetes | 编排器 | 自定义资源 |
Marathon | 编排器 | 标签 |
Rancher | 编排器 | 标签 |
总的来说,上述这些都是 Traefik 配置发现的方式,即 如何让你写的配置文件或者命令标签等被 Traefik 静态或者动态地读取生效 。基于篇幅原因,我们不完全例举所有的使用方式,下文主要演示一个完整的 Kubernetes 通过自定义资源方式的例子,其余方式大家可以自己参考官方文档: doc.traefik.io/traefik/pro...
5. Traefik 在 Kubernetes 中通过自定义资源对象实现 IngressRoute
首先我们需要定义自己的资源类型(IngressRoute/IngressRouteTCP/Middleware/TLSOption):
TraefikCRD.yml
yml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: middlewares.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: Middleware
plural: middlewares
singular: middleware
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced
同时注意 RBAC 授权资源,后面将通过部署 serviceAccountName
引用它们:
TraefikRBAC.yml
yml
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: default
接下来是服务 Service,一个是 Traefik 本身,另一个是其路由的 Web 应用程序 whoami :
TraefikService.yml
yml
apiVersion: v1
kind: Service
metadata:
name: traefik
spec:
ports:
- protocol: TCP
name: web
port: 8000
- protocol: TCP
name: admin
port: 8080
- protocol: TCP
name: websecure
port: 4443
selector:
app: traefik
WhoamiService.yml
yml
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- protocol: TCP
name: web
port: 80
selector:
app: whoami
接下来是部署 Deployment,同样,一个是 Traefik 的 pod, 另一个是 whoami 应用程序的 pod:
TraefikDeployment.yml
yml
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: default
name: traefik-ingress-controller
---
kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.0
args:
- --api.insecure
- --accesslog
- --entrypoints.web.Address=:8000
- --entrypoints.websecure.Address=:4443
- --providers.kubernetescrd
- --certificatesresolvers.default.acme.tlschallenge
- --certificatesresolvers.default.acme.email=foo@you.com
- --certificatesresolvers.default.acme.storage=acme.json
# Please note that this is the staging Let's Encrypt server.
# Once you get things working, you should remove that whole line altogether.
- --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
ports:
- name: web
containerPort: 8000
- name: websecure
containerPort: 4443
- name: admin
containerPort: 8080
这里我们使用了 Let's Encrypt 来设置的 TLS 证书,Let's Encrypt 是一个线上免费证书颁发组织,能够颁发权威机构认证的证书。此组织已经是当前最受欢迎、用户数量最广的证书颁发组织。
WhoamiDeployment.yml
yml
kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: whoami
labels:
app: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: containous/whoami
ports:
- name: web
containerPort: 80
注意:这里我们 Traefik Pod 端口无法从集群外部访问,这样会导致 ACME TLS 配置失败,在阿里云上我们可以购买 SLB 负载均衡,并且设置各个节点机器的端口映射来解决。
前边的步骤可以理解为安装过程,现在我们才开始设置我们的路由信息:
IngressRoutes.yml
yml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: simpleingressroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`your.domain.com`) && PathPrefix(`/notls`)
kind: Rule
services:
- name: whoami
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`your.domain.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
certResolver: default
HTTP 的路由规则:
规则 | 描述 |
---|---|
Headers(key , value ) |
检查 headers 中是否有一个键为key 值为 value 的键值对 |
HeadersRegexp(key , regexp ) |
检查 headers 中是否有一个键为key ,值匹配正则表达式regexp 的键值对 |
Host(domain-1 , ...) |
检查请求的域名是否包含在给定的domains 域名中 |
HostRegexp(traefik.io , {subdomain:[a-z]+}.traefik.io , ...) |
检查请求的域名是否匹配给定的regexp 正则表达式。 |
Method(GET , ...) |
检查请求的方法是否包含在给定的methods (GET , POST , PUT , DELETE , PATCH ) 中 |
Path(/path , /articles/{category}/{id:[0-9]+} , ...) |
匹配确定的请求路径,它接受一系列文字和正则表达式路径。 |
PathPrefix(/products/ , /articles/{category}/{id:[0-9]+} ) |
匹配请求前缀路径,它接受一系列文字和正则表达式前缀路径。 |
Query(foo=bar , bar=baz ) |
匹配查询字符串参数,接受 key=value 的键值对序列。 |
TCP 的路由规则:
规则 | 描述 |
---|---|
HostSNI(`domain-1`, ...) |
检查服务名标识是否和给定的 domains 对应。 |
6. Traefik 特色的 Middlewares 中间件
Middlewares 中间件被附件到路由上,是一种在请求发送到你的 Service 服务之前(或者在服务的响应发送到客户端之前)调整请求的一种方法。
Traefik 内置了许多不同功能的中间件,其中一些可以修改请求,头信息,一些负责重定向,一些添加身份验证等等。
中间件可以通过链式组合的方式来适用各种情况。
可用中间件列表:
中间件 | 用途 | 范围 |
---|---|---|
AddPrefix | 添加一个 Path 前缀 | Path 修改器 |
BasicAuth | Basic auth 认证机制 | 安全, 认证 |
Buffering | 缓冲 request/response | 请求生命周期 |
Chain | 结合多个中间件 | 中间件工具 |
CircuitBreaker | 停止调用不健康的服务 | 请求生命周期 |
Compress | 压缩 response 响应 | 内容修改器 |
DigestAuth | 添加 Digest 身份验证 | 安全, 认证 |
Errors | 自定义错误页面 | 请求生命周期 |
ForwardAuth | 使用外部服务转发身份验证 | 安全, 认证 |
Headers | 添加/更新 头信息 | 安全 |
IPWhiteList | 现在允许的客户端 IP | 安全, 请求生命周期 |
InFlightReq | 限制同时连接的数量 | 安全, 请求生命周期 |
PassTLSClientCert | 在 Header 里面添加客户端证书 | 安全 |
RateLimit | 限制调用频率 | 安全, 请求生命周期 |
RedirectScheme | 客户端重定向 | 请求生命周期 |
RedirectRegex | 客户端重定向 | 请求生命周期 |
ReplacePath | 更改请求的 path 路径 | Path 修改器 |
ReplacePathRegex | 更改请求的 path 路径 | Path 修改器 |
Retry | 出现错误时自动重试请求 | 请求生命周期 |
StripPrefix | 更改请求的 path 路径 | Path 修改器 |
StripPrefixRegex | 更改请求的 path 路径 | Path 修改器 |
继续上边的例子,我们来看几个中间件在 Kubernetes 中的使用,CRD 和 RBAC 我们已经声明过了:
AddPrefix(添加一个 Path 前缀):
AddPrefix 中间件在转发请求之前更新请求的路径。
yml
# Prefixing with /foo
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-foo
spec:
addPrefix:
prefix: /foo
ForwardAuth(使用外部服务转发身份验证):
ForwardAuth 中间件将身份验证委托给外部服务。如果服务用 2XX 代码回答,则授予访问权限,并执行原始请求。否则,将返回来自身份验证服务器的响应。
以下请求属性会以 X-Forwarded- 头的形式转发给认证服务:
Property | Forward-Request Header |
---|---|
HTTP Method | X-Forwarded-Method |
Protocol | X-Forwarded-Proto |
Host | X-Forwarded-Host |
Request URI | X-Forwarded-Uri |
Source IP-Address | X-Forwarded-For |
yml
# Forward authentication to example.com
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-auth
spec:
forwardAuth:
address: https://example.com/auth
trustForwardHeader: true ## 可信任所有X-Forwarded-*标头
authResponseHeaders: ## 从认证服务器额外带到Response响应的Headers
- X-Auth-User
- X-Secret
authRequestHeaders: ## 从Request请求额外带到认证服务器的Headers
- "Accept"
- "X-CustomHeader"
authResponseHeadersRegex: ^X- ## 从Request请求额外带到认证服务器的Headers(正则表达式)
具体细节的中间件,大家可以自己参考官方文档: doc.traefik.io/traefik/mid...