在 Kubernetes 上使用 Traefik 暴露服务:高级篇¶
本指南建立在基础指南介绍的概念和配置之上。继续之前,请确保你已完成基础指南,并已在 Kubernetes 上部署可正常运行的 Traefik。
在本高级指南中,你将学习如何通过以下功能增强 Traefik 部署:
- 使用中间件设置安全响应头和访问控制
- 使用 Let's Encrypt 自动管理证书(IngressRoute)
- 使用 cert-manager 自动管理证书(Gateway API)
- 为有状态应用启用粘性会话
- 使用多层路由实现分层路由,处理复杂的身份认证场景(仅适用于 IngressRoute)
- 使用服务中间件在服务级别应用中间件
前提条件¶
- 已完成基础指南
- 已安装 Traefik Proxy 的 Kubernetes 集群
- 已配置
kubectl,可与集群交互 - 基础指南中的 Traefik 部署已正常运行
添加中间件¶
中间件可以在请求或响应经过 Traefik 时对其进行修改。下面添加两种实用的中间件:用于安全防护的 Headers,以及用于访问控制的 IP 允许列表。
创建中间件¶
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: secure-headers
namespace: default
spec:
headers:
frameDeny: true
sslRedirect: true
browserXssFilter: true
contentTypeNosniff: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ip-allowlist
namespace: default
spec:
ipAllowList:
sourceRange:
- 127.0.0.1/32
- 10.0.0.0/8 # Typical cluster network range
- 192.168.0.0/16 # Common local network range
将其保存为 middlewares.yaml,然后应用:
kubectl apply -f middlewares.yaml
在 Gateway API 中应用中间件¶
在 Gateway API 中,可以使用 ExtensionRef 过滤器类型来应用中间件。这是 Gateway API 使用 Traefik 中间件的首选标准方式,因为它直接集成在 HTTPRoute 规范中。
现在更新 HTTPRoute,使用 ExtensionRef 过滤器引用这些中间件:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami
namespace: default
spec:
parentRefs:
- name: traefik-gateway
sectionName: websecure
hostnames:
- "whoami.docker.localhost"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: ExtensionRef
extensionRef: # Headers Middleware Definition
group: traefik.io
kind: Middleware
name: secure-headers
- type: ExtensionRef
extensionRef: # IP AllowList Middleware Definition
group: traefik.io
kind: Middleware
name: ip-allowlist
backendRefs:
- name: whoami-api
port: 80
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef: # Headers Middleware Definition
group: traefik.io
kind: Middleware
name: secure-headers
- type: ExtensionRef
extensionRef: # IP AllowList Middleware Definition
group: traefik.io
kind: Middleware
name: ip-allowlist
backendRefs:
- name: whoami
port: 80
更新 whoami-route.yaml 文件,然后应用:
kubectl apply -f whoami-route.yaml
这种方式使用 Gateway API 原生的过滤器机制,而非注解。ExtensionRef 过滤器类型允许你直接在 HTTPRoute 规范中引用 Traefik 中间件,更符合 Gateway API 的设计原则。
在 IngressRoute 中应用中间件¶
更新现有的 IngressRoute,添加中间件:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
kind: Rule
middlewares: # Middleware Definition
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami-api
port: 80
- match: Host(`whoami.docker.localhost`)
kind: Rule
middlewares: # Middleware Definition
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami
port: 80
tls:
certResolver: le
更新 whoami-ingressroute.yaml 文件,然后应用:
kubectl apply -f whoami-ingressroute.yaml
验证中间件的效果¶
检查安全响应头是否已应用:
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
你应该在响应中看到安全响应头,例如:
HTTP/2 200
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
strict-transport-security: max-age=31536000; includeSubDomains; preload
content-type: text/plain; charset=utf-8
content-length: 403
要测试 IP 允许列表,可以修改中间件中的 sourceRange,排除自己的 IP,然后验证访问是否被阻止。
使用 Let's Encrypt 生成证书¶
提示
Traefik 内置的 Let's Encrypt 集成支持 IngressRoute,但不会自动为 Gateway API 监听器签发证书。对于 Gateway API,应使用 cert-manager 或其他证书控制器。
在 IngressRoute 中使用 Let's Encrypt¶
在 Traefik 的 values.yaml 中配置证书解析器:
additionalArguments:
- "[email protected]" #replace with your email
- "--certificatesresolvers.le.acme.storage=/data/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
需要公共 DNS
Let's Encrypt 可能需要一个可公开访问的域名来验证域名所有权。使用 whoami.docker.localhost 等本地域名测试时,证书仍为自签名证书。在生产环境中,请替换为真实域名,并为其配置可公开访问、指向 Traefik 实例的 DNS 记录。
使用此配置更新 Traefik 安装:
helm upgrade traefik traefik/traefik -n traefik --reuse-values -f values.yaml
更新 IngressRoute,使用 Let's Encrypt 证书:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
kind: Rule
middlewares:
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami-api
port: 80
- match: Host(`whoami.docker.localhost`)
kind: Rule
middlewares:
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami
port: 80
tls:
certResolver: le
应用配置:
kubectl apply -f whoami-ingressroute.yaml
在 Gateway API 中使用 cert-manager¶
对于 Gateway API,安装 cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.0/cert-manager.yaml
创建 Issuer 和 Certificate:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
email: [email protected] # replace with your email
server: https://acme-v02-staging.api.letsencrypt.org/directory # Replace with the production server in production
privateKeySecretRef:
name: letsencrypt-account-key
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: traefik
namespace: default
kind: Gateway
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: whoami
namespace: default
spec:
secretName: whoami-tls-le # Name of secret where the generated certificate will be stored.
dnsNames:
- "whoami.docker.localhost" # Replace a real domain
issuerRef:
name: letsencrypt
kind: Issuer
需要公共 DNS
Let's Encrypt 需要一个可公开访问的域名来验证所有权。使用 whoami.docker.localhost 等本地域名时,cert-manager 会尝试执行验证,但验证将失败,证书仍为自签名证书。在生产环境中,请替换为具有公共 DNS 记录的域名,并将该记录指向集群的入口。
保存 YAML 文件,然后应用:
kubectl apply -f letsencrypt-issuer-andwhoami-certificate.yaml
现在更新 Gateway,使用生成的证书:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: traefik-gateway
namespace: default
spec:
gatewayClassName: traefik
listeners:
- name: web
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
- name: websecure
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: All
tls:
certificateRefs:
- name: whoami-tls-le # References the secret created by cert-manager
应用更新后的 Gateway:
kubectl apply -f gateway.yaml
现在,现有 HTTPRoute 在连接到启用了安全连接的网关监听器时,会使用此证书。
验证 Let's Encrypt 证书¶
证书签发后,可以这样验证:
# Check certificate status
kubectl get certificate -n default
# Verify the certificate chain
curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate"
你应该看到证书由 Let's Encrypt 签发。
配置粘性会话¶
粘性会话可确保同一用户的请求始终发送到同一台后端服务器,这对维护会话状态的应用至关重要。下面为 whoami 服务实现粘性会话。
首先,扩容 Deployment¶
为了演示粘性会话,先将 Deployment 扩容到 3 个副本:
kubectl scale deployment whoami --replicas=3
通过 TraefikService 在 Gateway API 中实现粘性会话¶
首先,创建用于粘性会话的 TraefikService:
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: whoami-sticky
namespace: default
spec:
weighted:
services:
- name: whoami
port: 80
weight: 1
sticky:
cookie:
name: sticky_cookie
secure: true
httpOnly: true
将其保存为 whoami-sticky-service.yaml,然后应用:
kubectl apply -f whoami-sticky-service.yaml
现在更新 HTTPRoute,通过注解引用 TraefikService:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami
namespace: default
spec:
parentRefs:
- name: traefik-gateway
sectionName: websecure
hostnames:
- "whoami.docker.localhost"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: ExtensionRef
extensionRef: # Headers Middleware Definition
group: traefik.io
kind: Middleware
name: secure-headers
- type: ExtensionRef
extensionRef: # IP AllowList Middleware Definition
group: traefik.io
kind: Middleware
name: ip-allowlist
backendRefs:
- name: whoami-api
port: 80
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: traefik.io # <── tell Gateway this is a TraefikService
kind: TraefikService
name: whoami-sticky
filters:
- type: ExtensionRef
extensionRef: # Headers Middleware Definition
group: traefik.io
kind: Middleware
name: secure-headers
- type: ExtensionRef
extensionRef: # IP AllowList Middleware Definition
group: traefik.io
kind: Middleware
name: ip-allowlist
backendRefs:
- name: whoami
port: 80
更新 whoami-route.yaml 文件,然后应用:
kubectl apply -f whoami-route.yaml
通过 TraefikService 在 IngressRoute 中实现粘性会话¶
首先,创建用于粘性会话的 TraefikService:
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: whoami-sticky
namespace: default
spec:
weighted:
services:
- name: whoami
port: 80
sticky:
cookie:
name: sticky_cookie
secure: true
httpOnly: true
将其保存为 whoami-sticky-service.yaml,然后应用:
kubectl apply -f whoami-sticky-service.yaml
现在更新 IngressRoute,使其使用此 TraefikService:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
kind: Rule
middlewares: # Middleware Definition
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami-api
port: 80
- match: Host(`whoami.docker.localhost`)
kind: Rule
middlewares: # Middleware Definition
- name: secure-headers
- name: ip-allowlist
services:
- name: whoami-sticky # Changed from whoami to whoami-sticky
kind: TraefikService # Added kind: TraefikService
tls:
certResolver: le
更新 whoami-ingressroute.yaml 文件,然后应用:
kubectl apply -f whoami-ingressroute.yaml
测试粘性会话¶
你可以发送多个请求,并观察它们是否都到达同一个后端 Pod,以测试粘性会话:
# First request - save cookies to a file
curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
# Subsequent requests - use the cookies
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
留意每个响应中的 Hostname 字段:使用 cookie 文件时,各次请求中的值应该保持一致,这说明粘性会话正在生效。
作为对比,可以尝试不携带 cookie 发送请求:
# Requests without cookies should be load-balanced across different pods
curl -k -H "Host: whoami.docker.localhost" https://localhost/
curl -k -H "Host: whoami.docker.localhost" https://localhost/
你应该会在这些响应中看到不同的 Hostname 值,因为每个请求都会经负载均衡分发到不同的 Pod。
浏览器测试
在浏览器中测试时,需要保持同一个浏览器会话,以保留 cookie。为确保安全,cookie 设置了 httpOnly 和 secure 标志,因此只会通过 HTTPS 连接发送,且无法通过 JavaScript 访问。
更多高级配置选项请参阅参考文档。
设置多层路由¶
多层路由允许路由器之间建立层级关系:父路由器可以先通过中间件处理请求,再由子路由器作出最终的路由决策。这对基于身份认证的路由或分阶段应用中间件特别有用。
IngressRoute 支持
Kubernetes IngressRoute(CRD)通过 spec.parentRefs 字段原生支持多层路由。标准 Kubernetes Ingress 和 Gateway API 资源不支持此功能。
基于身份认证的路由示例¶
下面创建一个多层路由配置,由父 IngressRoute 对请求进行身份认证,子 IngressRoute 则根据用户角色分发流量。
父路由器要求
多层路由中的父路由器不得定义服务。子路由器将根据各自的匹配规则选择服务。请确保所有子 IngressRoute 都通过 parentRefs 正确引用父路由器。
首先,部署后端服务:
# whoami-backends.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: admin-backend
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: admin-backend
template:
metadata:
labels:
app: admin-backend
spec:
containers:
- name: whoami
image: traefik/whoami
env:
- name: WHOAMI_NAME
value: "Admin Backend"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: admin-backend
namespace: default
spec:
selector:
app: admin-backend
ports:
- port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-backend
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: user-backend
template:
metadata:
labels:
app: user-backend
spec:
containers:
- name: whoami
image: traefik/whoami
env:
- name: WHOAMI_NAME
value: "User Backend"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: user-backend
namespace: default
spec:
selector:
app: user-backend
ports:
- port: 80
应用后端服务配置:
kubectl apply -f whoami-backends.yaml
现在创建多层路由所需的中间件和 IngressRoute:
# mlr-ingressroute.yaml
apiVersion: v1
kind: Secret
metadata:
name: auth-secret
namespace: default
type: Opaque
stringData:
users: |
admin:$apr1$DmXR3Add$wfdbGw6RWIhFb0ffXMM4d0
user:$apr1$GJtcIY1o$mSLdsWYeXpPHVsxGDqadI.
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: auth-middleware
namespace: default
spec:
basicAuth:
secret: auth-secret
headerField: X-Auth-User
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-parent
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.docker.localhost`) && PathPrefix(`/api`)
kind: Rule
middlewares:
- name: auth-middleware
# Note: No services and no TLS config - this is a parent IngressRoute
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-admin
namespace: default
spec:
parentRefs:
- name: api-parent
namespace: default # Optional, defaults to same namespace
routes:
- match: HeadersRegexp(`X-Auth-User`, `admin`)
kind: Rule
services:
- name: admin-backend
port: 80
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-user
namespace: default
spec:
parentRefs:
- name: api-parent
namespace: default # Optional, defaults to same namespace
routes:
- match: HeadersRegexp(`X-Auth-User`, `user`)
kind: Rule
services:
- name: user-backend
port: 80
生成密码哈希
上面的 密码哈希使用 htpasswd 生成。若要创建自己的用户凭据:
# Using htpasswd (Apache utils)
htpasswd -nb admin yourpassword
应用多层路由配置:
kubectl apply -f mlr-ingressroute.yaml
测试多层路由¶
测试路由行为:
# Request goes through parent router → auth middleware → admin child router
curl -k -u admin:test -H "Host: api.docker.localhost" https://localhost/api
以 admin 身份认证时,你应该看到 admin-backend 服务的响应。尝试使用 user:test 凭据,则会访问 user-backend 服务。
工作原理¶
- 请求到达
api.docker.localhost/api - 父 IngressRoute(
api-parent)根据主机和路径匹配请求 - BasicAuth 中间件对用户进行身份认证,并将用户名写入
X-Auth-User请求头 - 子 IngressRoute(
api-admin或api-user)根据请求头的值进行匹配 - 请求被转发到相应的 Kubernetes 服务
跨命名空间的父级引用¶
你可以通过指定 namespace 字段,引用其他命名空间中的父 IngressRoute:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-child
namespace: app-namespace
spec:
parentRefs:
- name: api-parent
namespace: shared-namespace # Parent in different namespace
routes:
- match: Path(`/child`)
kind: Rule
services:
- name: child-service
port: 80
跨命名空间的要求
要使用跨命名空间的父级引用,必须在 Traefik Helm values 中启用 allowCrossNamespace 选项:
providers:
kubernetesCRD:
allowCrossNamespace: true
引用多个父级¶
子 IngressRoute 可以引用多个父 IngressRoute:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-child
namespace: default
spec:
parentRefs:
- name: parent-one
- name: parent-two
routes:
- match: Path(`/api`)
kind: Rule
services:
- name: child-service
port: 80
有关多层路由的更多详细信息,请参阅多层路由文档。
服务中间件¶
服务中间件允许你将中间件应用于服务,而不必逐个配置路由器。这意味着,无论由哪个路由器转发请求,中间件都会对该服务处理的所有请求生效。
当你希望对到达服务的所有流量应用相同的中间件(如响应头、速率限制或身份认证),又不想在每个路由器上分别配置时,这一功能就很有用。
何时使用服务中间件¶
以下情况适合使用服务中间件:
- 多个路由器将流量转发到同一个服务,并且都需要应用相同的中间件
- 你希望确保无论流量如何到达服务,该服务始终应用某个中间件
- 你希望在服务级别集中配置中间件,以便管理
服务级别与路由器级别的中间件
- 路由器级别的中间件:仅在流量匹配该路由器的规则时应用
- 服务级别的中间件:应用于到达服务的所有流量,无论由哪个路由器转发
同时配置两者时,先执行路由器中间件,再执行服务中间件。
在 IngressRoute 中使用服务中间件¶
在 IngressRoute 中,可以将中间件直接附加到路由中的服务引用上:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: service-headers
namespace: default
spec:
headers:
customRequestHeaders:
X-Service-Middleware: "applied"
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`whoami.docker.localhost`)
kind: Rule
services:
- name: whoami
port: 80
middlewares:
- name: service-headers
tls: {}
将其保存为 service-middleware-ingressroute.yaml,然后应用:
kubectl apply -f service-middleware-ingressroute.yaml
在 Gateway API 中使用后端过滤器¶
Gateway API 支持通过 backendRefs[].filters 字段,直接为各个后端应用过滤器,从而实现后端级别的请求修改。
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: service-headers
namespace: default
spec:
headers:
customRequestHeaders:
X-Service-Middleware: "applied"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami
namespace: default
spec:
parentRefs:
- name: traefik-gateway
sectionName: websecure
hostnames:
- "whoami.docker.localhost"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: whoami
port: 80
filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: service-headers
Gateway API 也支持原生的 RequestHeaderModifier 过滤器类型,可用于较简单的请求头修改:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami
namespace: default
spec:
parentRefs:
- name: traefik-gateway
sectionName: websecure
hostnames:
- "whoami.docker.localhost"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: whoami
port: 80
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Backend-Header
value: "gateway-api-filter"
保存并应用:
kubectl apply -f service-middleware-gateway.yaml
在 Kubernetes Ingress 中使用服务注解¶
对于标准 Kubernetes Ingress,可以通过注解为服务应用中间件:
apiVersion: v1
kind: Service
metadata:
name: whoami
namespace: default
annotations:
traefik.ingress.kubernetes.io/service.middlewares: default-service-headers@kubernetescrd
spec:
selector:
app: whoami
ports:
- port: 80
注解值采用 <namespace>-<middleware-name>@kubernetescrd 格式。
启用 safeNaming 后,该值变为 <namespace>_<middleware-name>@kubernetescrd,使用与提供者生成的其他名称相同的 _ 分隔符。
测试服务中间件¶
验证服务中间件是否正常工作:
curl -k -H "Host: whoami.docker.localhost" https://localhost/
在 whoami 的响应中,你应该看到服务中间件添加的自定义请求头:
X-Service-Middleware: applied
有关服务中间件的更多详细信息,请参阅参考文档。
总结¶
在本高级指南中,你已学会:
- 使用安全响应头、IP 允许列表等中间件增强安全性
- 使用 Let's Encrypt(IngressRoute)和 cert-manager(Gateway API)自动管理证书
- 为有状态应用实现粘性会话
- 设置多层路由,实现基于身份认证的路由(仅适用于 IngressRoute)
- 在服务级别应用中间件,集中管理中间件配置
这些高级功能使你能够在 Kubernetes 上构建可用于生产的 Traefik 部署。你还可以进一步定制各项功能,以满足具体需求。
后续步骤¶
现在你已掌握 Kubernetes 上 Traefik 的基础和高级功能,还可以继续探索:
- 高级路由选项,例如查询参数匹配、基于请求头的路由等
- 其他中间件,用于身份认证、速率限制和请求修改
- 可观测性功能,用于监控和调试 Traefik 部署
- TCP 服务,用于暴露 TCP 服务
- UDP 服务,用于暴露 UDP 服务
- Kubernetes 提供者文档,了解 Kubernetes 集成的更多详细信息
- Gateway API 提供者文档,了解 Gateway API 集成的更多详细信息