一、入门Traefik系列------基础简介
官方文档
https://doc.traefik.io/traefik/\[1\]
简介
Traefik是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file...) 来自动化、动态的应用它的配置文件设置。
流量示意图
核心概念
当请求Traefik时,请求首先到entrypoints
,然后分析传入的请求,查看他们是否与定义的Routers
匹配。如果匹配,则会通过一系列middlewares
处理,再到traefikServices
上做流量转发,最后请求到kubernetes的services上
。
-
Providers
用来自动发现平台上的服务,可以是编排工具、容器引擎云提供商或者键值存储。Traefik通过查询Providers的API来查询路由的相关信息,一旦检测到变化,就会动态的更新路由。 -
Entrypoints
监听传入的流量,是网络的入口点,定义了接受请求的端口(HTTP或者TCP) -
Routers
分析请求(host,path,headers,SSL等),负责将传入的请求连接到可以处理这些请求的服务上去。 -
Middlewares
中间件,用来修改请求或者根据请求来做出判断,中间件被附件到路由上,是一种在请求发送到服务之前调整请求的一种方法。 -
Service
将请求转发给应用,负责配置如何最终将处理传入请求的实际服务,Traefik的Service介于Middlewares与KubernetesService之间,可以实现加权负载、流量复制等功能。
traefik组件与nginx类比
组件名称 | 功能 | nginx相同概念 |
---|---|---|
Providers | 监听路由信息变化,更新路由 | 修改nginx配置,reload服务。 |
Entrypoints | 网络入口,监听传入的流量 | 配置文件listen指定监听端口 |
Routers | 分析传入的请求,匹配规则 | 配置文件server_name+location |
Middlewares | 中间件,修改请求或响应 | location配置段中添加的缓存、压缩、请求头等配置 |
Service | 请求转发 | http配置段中的upstream |
Nginx-Ingress和traefik区别
Ingress Controller
k8s 是通过一个又一个的 controller 来负责监控、维护集群状态。Ingress Controller 就是监控 Ingress 路由规则的一个变化,然后跟 k8s 的资源操作入口 api-server 进行通信交互。K8s 并没有自带 Ingress Controller,它只是一种标准,具体实现有多种,需要自己单独安装,常用的是 Nginx Ingress Controller 和 Traefik Ingress Controller。Ingress Controller 收到请求,匹配 Ingress 转发规则,匹配到了就转发到后端 Service,而 Service 可能代表的后端 Pod 有多个,选出一个转发到那个 Pod,最终由那个 Pod 处理请求。
与kubernetes交互
nginx-ingress:由于微服务架构以及 Docker 技术和 kubernetes 编排工具最近几年才开始逐渐流行,所以一开始的反向代理服务器比如 nginx、apache 并未提供其支持,所以才会出现 Ingress Controller 这种东西来做 kubernetes 和前端负载均衡器如 nginx 之间做衔接;**即 Ingress Controller 的存在就是为了能跟 kubernetes 交互,然后写入nginx 配置,最后reload。**使用nginx作为前端负载均衡,通过ingress controller不断的和kubernetes api交互,实时获取后端service,pod等的变化,然后动态更新nginx配置,并刷新使配置生效,达到服务发现的目的。 traefik:traefik本身设计的就能够实时跟kubernetes api交互,感知后端service,pod等的变化,自动更新配置并重载。
traefik优点
-
不需要安装其他依赖,使用 GO 语言编译可执行文件
-
支持多种后台,如 Docker, Swarm mode, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS 等等
-
支持 REST API
-
配置文件热重载,可自动监听配置改动、发现新服务,并自动更新无需人工重启
-
支持熔断、限流功能
-
支持轮训、负载均衡
-
提供简洁的 UI 界面
-
支持 Websocket, HTTP/2, GRPC
-
自动更新 HTTPS 证书
-
支持高可用集群模式
Nginx和Traefik横向对比
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探测 | 重试、超时、心跳探测 | 重试、超时、心跳探测、熔断 |
负载均衡算法 | RR、会话保持、最小连接、最短时间、一致性hash | WRR、动态RR、会话保持 |
优点 | 简单易用,易接入 | Golang编写,部署容易,支持众多的后端,内置WebUI |
缺点 | 没有解决nginx reload,插件多,但是扩展性能差 | 性能略逊于NGINX,但强于HAProxy |
二、入门Traefik系列------部署与配置
helm部署
参考文档
官方文档:https://doc.traefik.io/traefik/getting-started/install-traefik/\[1\]
gtihub地址:https://github.com/traefik/traefik-helm-chart\[2\]
必要条件
Kubernetes版本1.14+
Helm版本3+
安装traefik
# 添加repo
[root@k8s-master traefik]# helm repo add traefik https://helm.traefik.io/traefik
# 更新repo仓库资源
[root@k8s-master traefik]# helm repo update
# 查看repo仓库traefik
[root@k8s-master traefik]# helm search repo traefik
NAME CHART VERSION APP VERSION DESCRIPTION
stable/traefik 1.87.7 1.7.26 DEPRECATED - A Traefik based Kubernetes ingress...
traefik/traefik 10.24.3 2.8.5 A Traefik based Kubernetes ingress controller# 创建traefik-v2名称空间
# 创建traefik名称空间
[root@k8s-master traefik]# kubectl create ns traefik
# 安装traefik
[root@k8s-master traefik]# helm install --namespace=traefik traefik traefik/traefik
# 查看helm列表
[root@k8s-master traefik]# helm list -n traefik
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
traefik traefik 1 2022-09-22 11:35:22.176916449 +0800 CST deployed traefik-10.24.3 2.8.5
# 查看pod资源信息
[root@k8s-master traefik]# kubectl get pod -n traefik
NAME READY STATUS RESTARTS AGE
traefik-5bfc574f88-vz4zr 1/1 Running 0 65s
端口转发dashboard服务
默认情况下,由于安全考虑,不会公开 Traefik 仪表板。可以通过端口转发实现仪表板访问
kubectl port-forward -n traefik deployment/traefik --address 0.0.0.0 9000:9000
# 可通过网址访问:http://k8s-master:9000/dashboard/
# 如果出现以下报错信息无法访问
Forwarding from 0.0.0.0:9000 -> 9000
Handling connection for 9000
E1229 13:10:40.655810 243434 portforward.go:400] an error occurred forwarding 9000 -> 9000: error forwarding port 9000 to pod de96185927c5a2a6150c67130c14ef0f97f14900144389122570ccfc4b5b3179, uid : unable to do port forwarding: socat not found
Handling connection for 9000
E1229 13:11:06.847006 243434 portforward.go:400] an error occurred forwarding 9000 -> 9000: error forwarding port 9000 to pod de96185927c5a2a6150c67130c14ef0f97f14900144389122570ccfc4b5b3179, uid : unable to do port forwarding: socat not found
Handling connection for 9000
# 解决办法在node节点安装socat
yum install socat
域名访问dashboard服务
使用helm部署的traefik默认使用LoadBalancer暴露服务,如果想使用此方式访问,首先要部署MetalLB才能分配到EXTERNAL-IP,
[root@k8s-master traefik]# ls
dashboard-ingress.yaml dashboard-svc.yaml dashboard.yaml
[root@k8s-master traefik]# kubectl get svc -n traefik
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.97.204.137 192.168.10.201 80:31718/TCP,443:31294/TCP 20m
然后配置dashboard的ingress资源:kubectl apply -f dashboard.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
namespace: traefik
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
接下来使用集群外部机器访问,添加hosts解析
192.168.10.201 traefik.test.com
# ip 为LoadBalancer的EXTERNAL-IP,域名为ingress配置的域名
image.png
YAML自定义部署
helm虽然实现了一键安装部署,但是查看helm包的value.yaml配置发现总共有500多行配置,当需要修改配置项或者对traefik做一下自定义配置时,并不灵活。如果只是使用traefik的基础功能,推荐使用helm部署。如果想深入研究使用traefik的话,推荐使用自定义方式部署,系列文章后续均使用自定义方式做演示。
环境准备
k8s版本:1.19.16
traefik版本:2.8.7
其中master节点充当边缘节点,安装两块网卡,eth0:k8s集群内网ip eth1公网ip
官方文档:https://doc.traefik.io/traefik/providers/kubernetes-crd/\[4\]
创建CRD资源
这里要注意你的k8s版本,从k8s 1.16开始废弃apiextensions.k8s.io/v1beta1,1.22完全删除。对于k8s 1.16 以上版本,使用apiextensions.k8s.io/v1
[root@k8s-master traefik]# wget https://raw.githubusercontent.com/traefik/traefik/v2.8/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
[root@k8s-master traefik]# kubectl apply -f kubernetes-crd-definition-v1.yml
[root@k8s-master traefik]# kubectl get crd
NAME CREATED AT
ingressroutes.traefik.containo.us 2022-09-22T10:01:07Z
ingressroutetcps.traefik.containo.us 2022-09-22T10:01:07Z
ingressrouteudps.traefik.containo.us 2022-09-22T10:01:07Z
ipaddresspools.metallb.io 2022-09-23T01:14:18Z
l2advertisements.metallb.io 2022-09-23T01:14:18Z
middlewares.traefik.containo.us 2022-09-22T10:01:07Z
middlewaretcps.traefik.containo.us 2022-09-22T10:01:07Z
serverstransports.traefik.containo.us 2022-09-22T10:01:07Z
tlsoptions.traefik.containo.us 2022-09-22T10:01:08Z
tlsstores.traefik.containo.us 2022-09-22T10:01:08Z
traefikservices.traefik.containo.us 2022-09-22T10:01:08Z
创建RBAC资源
[root@k8s-master traefik]# wget https://raw.githubusercontent.com/traefik/traefik/v2.8/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
dashboard-ingress.yaml kubernetes-crd-definition-v1.yml
[root@k8s-master traefik]# vim kubernetes-crd-rbac.yml
[root@k8s-master traefik]# kubectl apply -f kubernetes-crd-rbac.yml
[root@k8s-master traefik]# kubectl get clusterrole | grep traefik
traefik-ingress-controller 2022-09-23T14:10:06Z
[root@k8s-master traefik]# kubectl get clusterrolebinding | grep traefik
traefik-ingress-controller ClusterRole/traefik-ingress-controller 2m
创建traefik配置文件
在 Traefik 中有三种方式定义静态配置:在配置文件中、在命令行参数中、通过环境变量传递,由于 Traefik 配置很多,通过 CLI 定义不是很方便,一般时候选择将其配置选项放到配置文件中,然后存入 ConfigMap,将其挂入 traefik 中。参考文档:https://doc.traefik.io/traefik/getting-started/configuration-overview/
创建 traefik-config.yaml 文件
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.yaml: |-
global:
checkNewVersion: false # 周期性的检查是否有新版本发布
sendAnonymousUsage: false # 周期性的匿名发送使用统计信息
serversTransport:
insecureSkipVerify: true # Traefik忽略验证代理服务的TLS证书
api:
insecure: true # 允许HTTP 方式访问API
dashboard: true # 启用Dashboard
debug: false # 启用Debug调试模式
metrics:
prometheus: # 配置Prometheus监控指标数据,并使用默认配置
addRoutersLabels: true # 添加routers metrics
entryPoint: "metrics" # 指定metrics监听地址
entryPoints:
web:
address: ":80" # 配置80端口,并设置入口名称为web
forwardedHeaders:
insecure: true # 信任所有的forward headers
websecure:
address: ":443" # 配置443端口,并设置入口名称为 websecure
forwardedHeaders:
insecure: true
traefik:
address: ":9000" # 配置9000端口,并设置入口名称为 dashboard
metrics:
address: ":9100" # 配置9100端口,作为metrics收集入口
tcpep:
address: ":9200" # 配置9200端口,作为tcp入口
udpep:
address: ":9300/udp" # 配置9300端口,作为udp入口
providers:
kubernetesCRD: # 启用Kubernetes CRD方式来配置路由规则
ingressClass: ""
allowCrossNamespace: true #允许跨namespace
allowEmptyServices: true #允许空endpoints的service
log:
filePath: "/etc/traefik/logs/traefik.log" # 设置调试日志文件存储路径,如果为空则输出到控制台
level: "INFO" # 设置调试日志级别
format: "common" # 设置调试日志格式
accessLog:
filePath: "/etc/traefik/logs/access.log" # 设置访问日志文件存储路径,如果为空则输出到控制台
format: "common" # 设置访问调试日志格式
bufferingSize: 0 # 设置访问日志缓存行数
filters:
statusCodes: ["200"] # 设置只保留指定状态码范围内的访问日志
retryAttempts: true # 设置代理访问重试失败时,保留访问日志
minDuration: 20 # 设置保留请求时间超过指定持续时间的访问日志
fields: # 设置访问日志中的字段是否保留(keep保留、drop不保留)
defaultMode: keep # 设置默认保留访问日志字段
names: # 针对访问日志特别字段特别配置保留模式
ClientUsername: drop
StartUTC: drop # 禁用日志timestamp使用UTC
headers: # 设置Header中字段是否保留
defaultMode: keep # 设置默认保留Header中字段
names: # 针对Header中特别字段特别配置保留模式
#User-Agent: redact # 可以针对指定agent
Authorization: drop
Content-Type: keep
创建 Traefik ConfigMap 资源kubectl apply -f traefik-config.yaml
节点设置label标签
模拟实际生产环境,假设集群中master节点安装两块网卡充当边缘节点,需要提前给master节点设置 Label,这样当程序部署时 Pod 会自动调度到设置 Label 的节点上。
master节点网络信息如下:
网卡名称 | ip | 用途 |
---|---|---|
ens33 | 192.168.10.10 | k8s集群内部ip |
ens160 | 192.168.93.128 | 公网ip,用于外部访问 |
给master节点设置标签kubectl label nodes k8s-master IngressProxy=true
查看节点label信息
[root@k8s-master traefik]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready master 28h v1.19.16 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
k8s-work1 Ready <none> 28h v1.19.16 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-work1,kubernetes.io/os=linux
k8s-work2 Ready <none> 28h v1.19.16 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-work2,kubernetes.io/os=linux
Deployment部署traefik
使用DeamonSet或者Deployment均可部署,此处使用Deployment方式部署 Traefik,副本数设置为1,调度至IngressProxy=true的那台master边缘节点,并使用host网络模式,提高网络入口的网络性能
创建 traefik 部署文件 traefik-deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: default
name: traefik-ingress-controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik-ingress-controller
namespace: default
labels:
app: traefik
spec:
replicas: 1 # 副本数为1,因为集群只设置一台master为边缘节点
selector:
matchLabels:
app: traefik
template:
metadata:
name: traefik
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 1
containers:
- name: traefik
image: traefik:v2.8.7
env:
- name: KUBERNETES_SERVICE_HOST # 手动指定k8s api,避免网络组件不稳定。
value: "192.168.10.10"
- name: KUBERNETES_SERVICE_PORT_HTTPS # API server端口
value: "6443"
- name: KUBERNETES_SERVICE_PORT # API server端口
value: "6443"
- name: TZ # 指定时区
value: "Asia/Shanghai"
ports:
- name: web
containerPort: 80
hostPort: 80 # 将容器端口绑定所在服务器的 80 端口
- name: websecure
containerPort: 443
hostPort: 443 # 将容器端口绑定所在服务器的 443 端口
- name: admin
containerPort: 9000 # Traefik Dashboard 端口
- name: metrics
containerPort: 9100 # metrics端口
- name: tcpep
containerPort: 9200 # tcp端口
- name: udpep
containerPort: 9300 # udp端口
securityContext: # 只开放网络权限
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args:
- --configfile=/etc/traefik/config/traefik.yaml
volumeMounts:
- mountPath: /etc/traefik/config
name: config
- mountPath: /etc/traefik/logs
name: logdir
- mountPath: /etc/localtime
name: timezone
readOnly: true
volumes:
- name: config
configMap:
name: traefik-config
- name: logdir
hostPath:
path: /data/traefik/logs
type: "DirectoryOrCreate"
- name: timezone #挂载时区文件
hostPath:
path: /etc/localtime
type: File
tolerations: # 设置容忍所有污点,防止节点被设置污点
- operator: "Exists"
hostNetwork: true # 开启host网络,提高网络入口的网络性能
nodeSelector: # 设置node筛选器,在特定label的节点上启动
IngressProxy: "true" # 调度至IngressProxy: "true"的节点
部署并查看deployment资源信息
[root@k8s-master traefik]# kubectl apply -f traefik-deployment.yaml
NAME READY STATUS RESTARTS AGE
traefik-ingress-controller-6c79ff9645-hh559 1/1 Running 0 29m
[root@k8s-master traefik]# kubectl get pod
NAME READY STATUS RESTARTS AGE
traefik-ingress-controller-6c79ff9645-hh559 1/1 Running 0 29m 14h
创建service资源
创建 traefik服务资源文件 traefik-svc.yaml
# traefik-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: traefik
spec:
type: NodePort ## 官网示例为ClusterIP,为方便演示,此处改为NodePort
selector:
app: traefik
ports:
- name: web
protocol: TCP
port: 80
targetPort: 80
- name: websecure
protocol: TCP
port: 443
targetPort: 443
- name: admin
protocol: TCP
port: 9000
targetPort: 9000
- name: metrics
protocol: TCP
port: 9100
targetPort: 9100
- name: tcpep
protocol: TCP
port: 9200
targetPort: 9200
- name: udpep
protocol: UDP
port: 9300
targetPort: 9300
部署并查看svc资源信息
[root@k8s-master traefik]# kubectl apply -f traefik-svc.yaml
[root@k8s-master traefik]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 14h
traefik NodePort 10.102.64.244 <none> 80:31843/TCP,443:30318/TCP,9000:32689/TCP,9100:31527/TCP,9200:32432/TCP,9300:30608/UDP 2m35s
NodePort方式访问dashboard
traefik的dashboard使用nodeport暴露服务,将9000端口映射为32689,现在访问http://192.168.10.10:32689/
dashboard配置http域名访问
Traefik 应用已经部署完成,treafik的Dashboard为svc类型是NodePort,接下来配置域名规则,模拟外部用户通过公网IP访问dashboard应用
Traefik创建路由规则有多种方式,比如:
-
原生Ingress写法
-
使用CRD IngressRoute方式
-
使用GatewayAPI的方式
相较于原生Ingress写法,ingressRoute是2.1以后新增功能,简单来说,他们都支持路径(path)路由和域名(host)HTTP路由,以及HTTPS配置,区别在于IngressRoute需要定义CRD扩展,但是它支持了TCP、UDP路由以及中间件等新特性,强烈推荐使用ingressRoute,GatewayAPI方式后续再详细介绍。
创建 Traefik Dashboard 路由规则文件 traefik-dashboard-route.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
创建ingress资源
[root@k8s-master traefik]# kubectl apply -f traefik-dashboard-route.yaml
[root@k8s-master traefik]# kubectl get ingressroute -n traefik
NAME AGE
traefik-dashboard-route 44m
[root@k8s-master traefik]# kubectl get ingressroute
NAME AGE
dashboard 19m
配置 Hosts,客户端想通过域名访问服务,必须要进行 DNS 解析,由于这里没有 DNS 服务器进行域名解析,所以直接修改客户端端的 hosts 文件,将 Traefik 所在的节点的 公网IP 和自定义 host 绑定。
打开电脑的 Hosts 配置文件,往其加入下面配置:192.168.93.128 traefik.test.com
配置完成后,打开浏览器输入地址:http://traefik.test.com 打开 Traefik Dashboard。
其他配置
强制使用TLS v1.2+
如今,TLS v1.0 和 v1.1 因为存在安全问题,现在已被弃用。为了保障系统安全,所有入口路由都应该强制使用TLS v1.2 或更高版本。
参考文档:https://doc.traefik.io/traefik/user-guides/crd-acme/#force-tls-v12
[root@k8s-master traefik]# cat tlsoption.yml
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: default
namespace: default
spec:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # TLS 1.2
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 # TLS 1.2
- TLS_AES_256_GCM_SHA384 # TLS 1.3
- TLS_CHACHA20_POLY1305_SHA256 # TLS 1.3
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
[root@k8s-master traefik]# kubectl apply -f tlsoption.yml
tlsoption.traefik.containo.us/default created
日志轮换
官方并没有日志轮换的功能,但是traefik收到USR1信号后会重建日志文件,因此可以通过logrotate实现日志轮换
参考文档:https://doc.traefik.io/traefik/observability/logs/\[8\]
在/etc/logrotate.d创建下traefik目录
mkdir -p /etc/logrotate.d/traefik
配置logrotate文件
/data/traefik/logs/*.log {
daily
rotate 15
missingok
notifempty
compress
dateext
dateyesterday
dateformat .%Y-%m-%d
create 0644 root root
postrotate
docker kill --signal="USR1" $(docker ps | grep traefik |grep -v pause| awk '{print $1}')
endscript
}
添加crontab定时任务
sudo echo "0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.d/traefik/traefikLogrotate >/dev/null 2>&1" > /etc/crontab
多控制器
有的业务场景下可能需要在一个集群中部署多个 traefik,不同的实例控制不同的 IngressRoute 资源对象,要实现该功能有两种方法:
通过 annotations 注解筛选
首先在traefik配置文件中的providers下增加Ingressclass参数,指定具体的值。
参考文档:https://doc.traefik.io/traefik/providers/kubernetes-crd/#ingressclass
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.yaml: |-
global:
checkNewVersion: false # 周期性的检查是否有新版本发布
sendAnonymousUsage: false # 周期性的匿名发送使用统计信息
serversTransport:
insecureSkipVerify: true # Traefik忽略验证代理服务的TLS证书
api:
insecure: true # 允许HTTP 方式访问API
dashboard: true # 启用Dashboard
debug: false # 启用Debug调试模式
metrics:
prometheus: # 配置Prometheus监控指标数据,并使用默认配置
addRoutersLabels: true # 添加routers metrics
entryPoint: "metrics" # 指定metrics监听地址
entryPoints:
web:
address: ":80" # 配置80端口,并设置入口名称为web
forwardedHeaders:
insecure: true # 信任所有的forward headers
websecure:
address: ":443" # 配置443端口,并设置入口名称为 websecure
forwardedHeaders:
insecure: true
traefik:
address: ":9000" # 配置9000端口,并设置入口名称为 dashboard
metrics:
address: ":9100" # 配置9100端口,作为metrics收集入口
tcpep:
address: ":9200" # 配置9200端口,作为tcp入口
udpep:
address: ":9300/udp" # 配置9300端口,作为udp入口
providers:
kubernetesCRD: # 启用Kubernetes CRD方式来配置路由规则
ingressClass: "traefik-v2.8" # 指定traefik的ingressClass实例名称
allowCrossNamespace: true #允许跨namespace
allowEmptyServices: true #允许空endpoints的service
log:
filePath: "/etc/traefik/logs/traefik.log" # 设置调试日志文件存储路径,如果为空则输出到控制台
level: "INFO" # 设置调试日志级别
format: "common" # 设置调试日志格式
accessLog:
filePath: "/etc/traefik/logs/access.log" # 设置访问日志文件存储路径,如果为空则输出到控制台
format: "common" # 设置访问调试日志格式
bufferingSize: 0 # 设置访问日志缓存行数
filters:
statusCodes: ["200"] # 设置只保留指定状态码范围内的访问日志
retryAttempts: true # 设置代理访问重试失败时,保留访问日志
minDuration: 20 # 设置保留请求时间超过指定持续时间的访问日志
fields: # 设置访问日志中的字段是否保留(keep保留、drop不保留)
defaultMode: keep # 设置默认保留访问日志字段
names: # 针对访问日志特别字段特别配置保留模式
ClientUsername: drop
StartUTC: drop # 禁用日志timestamp使用UTC
headers: # 设置Header中字段是否保留
defaultMode: keep # 设置默认保留Header中字段
names: # 针对Header中特别字段特别配置保留模式
#User-Agent: redact # 可以针对指定agent
Authorization: drop
Content-Type: keep
接下来在IngressRoute 资源对象中的annotations参数中添加 kubernetes.io/ingress.class: traefik-v2.8即可
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
annotations:
kubernetes.io/ingress.class: traefik-v2.8 # 因为静态配置文件指定了ingressclass,所以这里的annotations 要指定,否则访问会404
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
通过标签选择器进行过滤
首先在traefik配置文件中的providers下增加labelSelector参数,指定具体的标签键值。
参考文档:https://doc.traefik.io/traefik/providers/kubernetes-crd/#labelselector
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.yaml: |-
global:
checkNewVersion: false # 周期性的检查是否有新版本发布
sendAnonymousUsage: false # 周期性的匿名发送使用统计信息
serversTransport:
insecureSkipVerify: true # Traefik忽略验证代理服务的TLS证书
api:
insecure: true # 允许HTTP 方式访问API
dashboard: true # 启用Dashboard
debug: false # 启用Debug调试模式
metrics:
prometheus: # 配置Prometheus监控指标数据,并使用默认配置
addRoutersLabels: true # 添加routers metrics
entryPoint: "metrics" # 指定metrics监听地址
entryPoints:
web:
address: ":80" # 配置80端口,并设置入口名称为web
forwardedHeaders:
insecure: true # 信任所有的forward headers
websecure:
address: ":443" # 配置443端口,并设置入口名称为 websecure
forwardedHeaders:
insecure: true
traefik:
address: ":9000" # 配置9000端口,并设置入口名称为 dashboard
metrics:
address: ":9100" # 配置9100端口,作为metrics收集入口
tcpep:
address: ":9200" # 配置9200端口,作为tcp入口
udpep:
address: ":9300/udp" # 配置9300端口,作为udp入口
providers:
kubernetesCRD: # 启用Kubernetes CRD方式来配置路由规则
# ingressClass: "traefik-v2.8" # 指定traefik的ingressClass名称
labelSelector: "app=traefik-v2.8" # 通过标签选择器指定traefik标签
allowCrossNamespace: true #允许跨namespace
allowEmptyServices: true #允许空endpoints的service
log:
filePath: "/etc/traefik/logs/traefik.log" # 设置调试日志文件存储路径,如果为空则输出到控制台
level: "INFO" # 设置调试日志级别
format: "common" # 设置调试日志格式
accessLog:
filePath: "/etc/traefik/logs/access.log" # 设置访问日志文件存储路径,如果为空则输出到控制台
format: "common" # 设置访问调试日志格式
bufferingSize: 0 # 设置访问日志缓存行数
filters:
statusCodes: ["200"] # 设置只保留指定状态码范围内的访问日志
retryAttempts: true # 设置代理访问重试失败时,保留访问日志
minDuration: 20 # 设置保留请求时间超过指定持续时间的访问日志
fields: # 设置访问日志中的字段是否保留(keep保留、drop不保留)
defaultMode: keep # 设置默认保留访问日志字段
names: # 针对访问日志特别字段特别配置保留模式
ClientUsername: drop
StartUTC: drop # 禁用日志timestamp使用UTC
headers: # 设置Header中字段是否保留
defaultMode: keep # 设置默认保留Header中字段
names: # 针对Header中特别字段特别配置保留模式
#User-Agent: redact # 可以针对指定agent
Authorization: drop
Content-Type: keep
然后在 IngressRoute 资源对象中添加labels标签选择器,选择app: traefik-v2.8这个标签即可
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
labels: # 通过标签选择器,该IngressRoute资源由配置了app=traefik-v2.8的traefik处理
app: traefik-v2.8
# annotations:
# kubernetes.io/ingress.class: traefik-v2.8 # 因为静态配置文件指定了ingressclass,所以这里的annotations 要指定,否则访问会404
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
Traefik CRD功能总结
traefik通过自定义资源实现了对traefik资源的创建和管理,支持的crd资源类型如下所示:
参考文档:https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/
kind | 功能 |
---|---|
IngressRoute | HTTP路由配置 |
Middleware | HTTP中间件配置 |
TraefikService | HTTP负载均衡/流量复制配置 |
IngressRoute | TCP路由配置 |
MiddlewareTCP | TCP中间件配置 |
IngressRouteUDP | UDP路由配置 |
TLSOptions | TLS连接参数配置 |
TLSStores | TLS存储配置 |
ServersTransport | traefik与后端之间的传输配置 |
三、入门Traefik系列------路由配置与使用
环境准备
部署myapp1实例
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp1
spec:
selector:
matchLabels:
app: myapp1
template:
metadata:
labels:
app: myapp1
spec:
containers:
- name: myapp1
image: ikubernetes/myapp:v1
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myapp1
spec:
type: ClusterIP
selector:
app: myapp1
ports:
- port: 80
targetPort: 80
部署myapp2实例
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp2
spec:
selector:
matchLabels:
app: myapp2
template:
metadata:
labels:
app: myapp2
spec:
containers:
- name: myapp2
image: ikubernetes/myapp:v2
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myapp2
spec:
type: ClusterIP
selector:
app: myapp2
ports:
- port: 80
targetPort: 80
创建资源并访问测试
[root@k8s-master ingress]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp1-795d947b45-9lsm6 1/1 Running 0 2m18s
myapp2-6ffd54f76-ljkr9 1/1 Running 0 66s
[root@k8s-master ingress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 44h
myapp1 ClusterIP 10.104.91.200 <none> 80/TCP 2m26s
myapp2 ClusterIP 10.111.245.32 <none> 80/TCP 100s
[root@k8s-master ingress]# curl 10.104.91.200
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master ingress]# curl 10.111.245.32
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
路由简介
路由功能
参考文档:https://doc.traefik.io/traefik/routing/overview/
当启动Traefik时,需要定义entrypoints,然后通过entrypoints的路由来分析传入的请求,来查看他们是否是一组规则匹配,如果匹配,则路由可能将请求通过一系列的转换过来在发送到服务上去。
traefik支持的匹配规则
规则 | 描述 |
---|---|
Headers(key , value ) |
检查headers中是否有一个键为key值为value的键值对 |
HeadersRegexp(key , regexp ) |
检查headers中是否有一个键位key值为正则表达式匹配的键值对 |
Host(example.com , ...) |
检查请求的域名是否包含在特定的域名中 |
HostRegexp(example.com , {subdomain:[a-z]+}.example.com , ...) |
检查请求的域名是否包含在特定的正则表达式域名中 |
Method(GET , ...) |
检查请求方法是否为给定的methods(GET、POST、PUT、DELETE、PATCH)中 |
Path(/path , /articles/{cat:[a-z]+}/{id:[0-9]+} , ...) |
匹配特定的请求路径,它接受一系列文字和正则表达式路径 |
PathPrefix(/products/ , /articles/{cat:[a-z]+}/{id:[0-9]+} ) |
匹配特定的前缀路径,它接受一系列文字和正则表达式前缀路径 |
Query(foo=bar , bar=baz ) |
匹配查询字符串参数,接受key=value的键值对 |
ClientIP(10.0.0.0/16 , ::1 ) |
如果请求客户端 IP 是给定的 IP/CIDR 之一,则匹配。它接受 IPv4、IPv6 和网段格式。 |
路由配置(IngressRoute)
HTTP域名路由
实现目标:集群外部用户通过访问http://myapp1.test.com域名时,将请求代理至myapp1应用。创建ingressrouter规则文件
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp1
spec:
entryPoints:
- web
routes:
- match: Host(`myapp1.test.com`) # 域名
kind: Rule
services:
- name: myapp1 # 与svc的name一致
port: 80 # 与svc的port一致
创建资源
[root@k8s-master ingress]# kubectl apply -f myapp1-ingress.yaml
ingressroute.traefik.containo.us/myapp1 created
[root@k8s-master ingress]# kubectl get ingressroute
dashboard myapp1
[root@k8s-master ingress]# kubectl get ingressroute
NAME AGE
dashboard 4h26m
myapp1 20s
客户端添加hosts记录192.168.93.128 myapp1.test.com
,然后访问验证
HTTPS域名路由(自有证书)
公网服务的话,可以在云厂商那里购买证书。内部服务的话,就直接用 openssl 来创建一个自签名的证书即可,要注意证书文件名称必须是 tls.crt 和 tls.key。接下来演示自签证书的配置。创建自签证书
[root@k8s-master ingress]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=myapp2.test.com"
建Secret资源来引用证书文件
[root@k8s-master ingress]# kubectl create secret tls myapp2-tls --cert=tls.crt --key=tls.key
secret/myapp2-tls created
[root@k8s-master ingress]# kubectl describe secrets myapp2-tls
Name: myapp2-tls
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1131 bytes
tls.key: 1704 bytes
创建IngressRouter规则文件,集群外部用户通过访问https://myapp2.test.com域名时,将请求代理至myapp2应用。
[root@k8s-master ingress]# cat myapp2-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2
spec:
entryPoints:
- websecure # 监听 websecure 这个入口点,也就是通过 443 端口来访问
routes:
- match: Host(`myapp2.test.com`)
kind: Rule
services:
- name: myapp2
port: 80
tls:
secretName: myapp2-tls # 指定tls证书名称
创建资源
[root@k8s-master ingress]# kubectl apply -f myapp2-ingress.yaml
ingressroute.traefik.containo.us/myapp2 created
[root@k8s-master ingress]# kubectl get ingressroute
dashboard myapp2
[root@k8s-master ingress]# kubectl get ingressroute
NAME AGE
dashboard 5h11m
myapp1 45m
myapp2 2m55s
客户端添加hosts记录192.168.93.128 myapp2.test.com
,然后访问验证,由于我们是自签名的证书,所以证书是不受信任的。
HTTPS域名路由(自动生成证书)
Traefik除了使用自有证书外,还支持在创建ingress资源时自动请求Let's Encrypt生成证书,并且在证书过期前30天自动续订证书。要使用Let's Encrypt自动生成证书,需要使用ACME。需要在静态配置中定义 "证书解析器",Traefik负责从ACME服务器中检索证书。然后,每个 "路由器 "被配置为启用TLS,并通过tls.certresolver配置选项与一个证书解析器关联。如果使用tlsChallenge,则要求Let's Encrypt到 Traefik 443 端口必须是可达的。如果使用httpChallenge,则要求Let's Encrypt到 Traefik 80端口必须是可达的。如果使用dnsChallenge,只需要配置上 DNS 解析服务商的 API 访问密钥即可校验。
tlsChallenge/httpChallenge
使用tlsChallenge或者httpChallenge的前提条件是traefik所在节点可以正常访问Let's Encrypt网站,国内网络可能访问存在异常,并且配置的ingress域名已经设置了dns的A记录解析,指向traefik所在的节点公网IP地址才可以,否则申请成功证书后无法通过验证。修改traefik配置文件,新增certificatesResolvers配置
# traefik-config.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.yaml: |-
global:
checkNewVersion: false ## 周期性的检查是否有新版本发布
sendAnonymousUsage: false ## 周期性的匿名发送使用统计信息
serversTransport:
insecureSkipVerify: true ## Traefik忽略验证代理服务的TLS证书
api:
insecure: true ## 允许HTTP 方式访问API
dashboard: true ## 启用Dashboard
debug: false ## 启用Debug调试模式
metrics:
prometheus: ## 配置Prometheus监控指标数据,并使用默认配置
addRoutersLabels: true ## 添加routers metrics
entryPoint: "metrics" ## 指定metrics监听地址
entryPoints:
web:
address: ":80" ## 配置80端口,并设置入口名称为web
forwardedHeaders:
insecure: true ## 信任所有的forward headers
websecure:
address: ":443" ## 配置443端口,并设置入口名称为 websecure
forwardedHeaders:
insecure: true
traefik:
address: ":9000" ## 配置9000端口,并设置入口名称为 dashboard
metrics:
address: ":9100" ## 配置9100端口,作为metrics收集入口
tcpep:
address: ":9200" ## 配置9200端口,作为tcp入口
udpep:
address: ":9300/udp" ## 配置9300端口,作为udp入口
certificatesResolvers: ## 开启ACME自动续签证书
sample:
acme:
email: 1554382111@qq.com # 邮箱配置
storage: /etc/traefik/acme/acme.json # 保存 ACME 证书的位置
# tlsChallenge: {} # tlsChallenge模式续签
httpChallenge:
entryPoint: web # httpChallenge模式续签
providers:
kubernetesCRD: ## 启用Kubernetes CRD方式来配置路由规则
ingressClass: ""
allowCrossNamespace: true ##允许跨namespace
allowEmptyServices: true ##允许空endpoints的service
kubernetesIngress: ## 启动Kubernetes Ingress方式来配置路由规则
ingressClass: ""
log:
filePath: "/etc/traefik/logs/traefik.log" ## 设置调试日志文件存储路径,如果为空则输出到控制台
level: "DEBUG" ## 设置调试日志级别
format: "common" ## 设置调试日志格式
accessLog:
filePath: "/etc/traefik/logs/access.log" ## 设置访问日志文件存储路径,如果为空则输出到控制台
format: "common" ## 设置访问调试日志格式
bufferingSize: 0 ## 设置访问日志缓存行数
filters:
#statusCodes: ["200"] ## 设置只保留指定状态码范围内的访问日志
retryAttempts: true ## 设置代理访问重试失败时,保留访问日志
minDuration: 20 ## 设置保留请求时间超过指定持续时间的访问日志
fields: ## 设置访问日志中的字段是否保留(keep保留、drop不保留)
defaultMode: keep ## 设置默认保留访问日志字段
names: ## 针对访问日志特别字段特别配置保留模式
ClientUsername: drop
StartUTC: drop ## 禁用日志timestamp使用UTC
headers: ## 设置Header中字段是否保留
defaultMode: keep ## 设置默认保留Header中字段
names: ## 针对Header中特别字段特别配置保留模式
#User-Agent: redact ## 可以针对指定agent
Authorization: drop
Content-Type: keep
修改traefik的deployment资源文件,挂载acme目录存储ACME证书信息
# traefik-deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: default
name: traefik-ingress-controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik-ingress-controller
namespace: default
labels:
app: traefik
spec:
replicas: 1 #副本数为1,因为集群只设置一台master为边缘节点
selector:
matchLabels:
app: traefik
template:
metadata:
name: traefik
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 1
containers:
- name: traefik
image: traefik:v2.8.7
env:
- name: KUBERNETES_SERVICE_HOST ##手动指定k8s api,避免网络组件不稳定。
value: "192.168.10.10"
- name: KUBERNETES_SERVICE_PORT_HTTPS ## API server端口
value: "6443"
- name: KUBERNETES_SERVICE_PORT ## API server端口
value: "6443"
- name: TZ ##指定时区
value: "Asia/Shanghai"
ports:
- name: web
containerPort: 80
hostPort: 80 ## 将容器端口绑定所在服务器的 80 端口
- name: websecure
containerPort: 443
hostPort: 443 ## 将容器端口绑定所在服务器的 443 端口
- name: admin
containerPort: 9000 ## Traefik Dashboard 端口
- name: metrics
containerPort: 9100 ## metrics端口
- name: tcpep
containerPort: 9200 ## tcp端口
- name: udpep
containerPort: 9300 ## udp端口
securityContext: ## 只开放网络权限
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args:
- --configfile=/etc/traefik/config/traefik.yaml
volumeMounts:
- mountPath: /etc/traefik/config
name: config
- mountPath: /etc/traefik/logs
name: logdir
- mountPath: /etc/localtime
name: timezone
readOnly: true
- mountPath: /etc/traefik/acme
name: acme
volumes:
- name: config
configMap:
name: traefik-config
- name: logdir
hostPath:
path: /data/traefik/logs
type: "DirectoryOrCreate"
- name: timezone #挂载时区文件
hostPath:
path: /etc/localtime
type: File
- name: acme # 自动续签证书文件
hostPath:
path: /data/traefik/acme
type: "DirectoryOrCreate"
tolerations: ## 设置容忍所有污点,防止节点被设置污点
- operator: "Exists"
hostNetwork: true ## 开启host网络,提高网络入口的网络性能
nodeSelector: ## 设置node筛选器,在特定label的节点上启动
IngressProxy: "true" ## 调度至IngressProxy: "true"的节点
配置IngressRouter规则
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2
spec:
entryPoints:
- websecure # 监听 websecure 这个入口点,也就是通过 443 端口来访问
routes:
- match: Host(`myapp2.cuiliangblog.cn`) # 必须是真实存在的域名,且配置了dns解析记录,指向traefik节点所在的公网IP
kind: Rule
services:
- name: myapp2
port: 80
tls:
certResolver: sample # 使用自动生成证书,名字与traefik的certificatesResolvers名称一致
dnsChallenge
dns 校验方式可以生成通配符的证书,只需要配置上 DNS 解析服务商的 API 访问密钥即可校验。每个厂商的配置都略有差异,此处以阿里云为例,其他厂商的配置请查看文档https://go-acme.github.io/lego/dns/\[3\]修改traefik配置文件,新增dnsChallenge配置
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.yaml: |-
global:
checkNewVersion: false ## 周期性的检查是否有新版本发布
sendAnonymousUsage: false ## 周期性的匿名发送使用统计信息
serversTransport:
insecureSkipVerify: true ## Traefik忽略验证代理服务的TLS证书
api:
insecure: true ## 允许HTTP 方式访问API
dashboard: true ## 启用Dashboard
debug: false ## 启用Debug调试模式
metrics:
prometheus: ## 配置Prometheus监控指标数据,并使用默认配置
addRoutersLabels: true ## 添加routers metrics
entryPoint: "metrics" ## 指定metrics监听地址
entryPoints:
web:
address: ":80" ## 配置80端口,并设置入口名称为web
forwardedHeaders:
insecure: true ## 信任所有的forward headers
websecure:
address: ":443" ## 配置443端口,并设置入口名称为 websecure
forwardedHeaders:
insecure: true
traefik:
address: ":9000" ## 配置9000端口,并设置入口名称为 dashboard
metrics:
address: ":9100" ## 配置9100端口,作为metrics收集入口
tcpep:
address: ":9200" ## 配置9200端口,作为tcp入口
udpep:
address: ":9300/udp" ## 配置9300端口,作为udp入口
certificatesResolvers: ## 开启ACME自动续签证书
sample:
acme:
email: 1554382111@qq.com # 邮箱配置
storage: /etc/traefik/acme/acme.json # 保存 ACME 证书的位置
# tlsChallenge: {} # tls模式续签
# httpChallenge:
# entryPoint: web # http模式续签
dnsChallenge: # dns模式续签证书
provider: alidns # 云厂商编号
delayBeforeCheck: 0 # ACME 验证之前,会验证 TXT 记录。设定延迟验证时间(以秒为单位)
providers:
kubernetesCRD: ## 启用Kubernetes CRD方式来配置路由规则
ingressClass: ""
allowCrossNamespace: true ##允许跨namespace
allowEmptyServices: true ##允许空endpoints的service
kubernetesIngress: ## 启动Kubernetes Ingress方式来配置路由规则
ingressClass: ""
log:
filePath: "/etc/traefik/logs/traefik.log" ## 设置调试日志文件存储路径,如果为空则输出到控制台
level: "DEBUG" ## 设置调试日志级别
format: "common" ## 设置调试日志格式
accessLog:
filePath: "/etc/traefik/logs/access.log" ## 设置访问日志文件存储路径,如果为空则输出到控制台
format: "common" ## 设置访问调试日志格式
bufferingSize: 0 ## 设置访问日志缓存行数
filters:
#statusCodes: ["200"] ## 设置只保留指定状态码范围内的访问日志
retryAttempts: true ## 设置代理访问重试失败时,保留访问日志
minDuration: 20 ## 设置保留请求时间超过指定持续时间的访问日志
fields: ## 设置访问日志中的字段是否保留(keep保留、drop不保留)
defaultMode: keep ## 设置默认保留访问日志字段
names: ## 针对访问日志特别字段特别配置保留模式
ClientUsername: drop
StartUTC: drop ## 禁用日志timestamp使用UTC
headers: ## 设置Header中字段是否保留
defaultMode: keep ## 设置默认保留Header中字段
names: ## 针对Header中特别字段特别配置保留模式
#User-Agent: redact ## 可以针对指定agent
Authorization: drop
Content-Type: keep
登录阿里云后台获取ALICLOUD_ACCESS_KEY、ALICLOUD_SECRET_KEY、ALICLOUD_REGION_ID信息 创建Secret 对象存放密钥信息,记得填写base64编码后的值
apiVersion: v1
kind: Secret
metadata:
name: alidns-secret
type: Opaque
data:
ALICLOUD_ACCESS_KEY: XXX
ALICLOUD_SECRET_KEY: XXX
ALICLOUD_REGION_ID: XXX
修改traefik的deployment资源清单,添加密钥env变量
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: default
name: traefik-ingress-controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik-ingress-controller
namespace: default
labels:
app: traefik
spec:
replicas: 1 # 副本数为1,因为集群只设置一台master为边缘节点
selector:
matchLabels:
app: traefik
template:
metadata:
name: traefik
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 1
containers:
- name: traefik
image: traefik:v2.8.7
env:
- name: KUBERNETES_SERVICE_HOST # 手动指定k8s api,避免网络组件不稳定。
value: "192.168.10.10"
- name: KUBERNETES_SERVICE_PORT_HTTPS # API server端口
value: "6443"
- name: KUBERNETES_SERVICE_PORT # API server端口
value: "6443"
- name: TZ # 指定时区
value: "Asia/Shanghai"
- name: ALICLOUD_ACCESS_KEY # 阿里云AK
valueFrom:
secretKeyRef:
name: alidns-secret
key: ALICLOUD_ACCESS_KEY
- name: ALICLOUD_SECRET_KEY # 阿里云SK
valueFrom:
secretKeyRef:
name: alidns-secret
key: ALICLOUD_SECRET_KEY
- name: ALICLOUD_REGION_ID # 阿里云资源区域编号
valueFrom:
secretKeyRef:
name: alidns-secret
key: ALICLOUD_REGION_ID
ports:
- name: web
containerPort: 80
hostPort: 80 # 将容器端口绑定所在服务器的 80 端口
- name: websecure
containerPort: 443
hostPort: 443 # 将容器端口绑定所在服务器的 443 端口
- name: admin
containerPort: 9000 # Traefik Dashboard 端口
- name: metrics
containerPort: 9100 # metrics端口
- name: tcpep
containerPort: 9200 # tcp端口
- name: udpep
containerPort: 9300 # udp端口
securityContext: # 只开放网络权限
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args:
- --configfile=/etc/traefik/config/traefik.yaml
volumeMounts:
- mountPath: /etc/traefik/config
name: config
- mountPath: /etc/traefik/logs
name: logdir
- mountPath: /etc/localtime
name: timezone
readOnly: true
- mountPath: /etc/traefik/acme
name: acme
volumes:
- name: config
configMap:
name: traefik-config
- name: logdir
hostPath:
path: /data/traefik/logs
type: "DirectoryOrCreate"
- name: timezone #挂载时区文件
hostPath:
path: /etc/localtime
type: File
- name: acme # 自动续签证书文件
hostPath:
path: /data/traefik/acme
type: "DirectoryOrCreate"
tolerations: # 设置容忍所有污点,防止节点被设置污点
- operator: "Exists"
hostNetwork: true # 开启host网络,提高网络入口的网络性能
nodeSelector: # 设置node筛选器,在特定label的节点上启动
IngressProxy: "true" # 调度至IngressProxy: "true"的节点
修改ingressrouter配置
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2
spec:
entryPoints:
- websecure # 监听 websecure 这个入口点,也就是通过 443 端口来访问
routes:
- match: Host(`myapp2.cuiliangblog.cn`)
kind: Rule
services:
- name: myapp2
port: 80
tls:
certResolver: sample # 使用自动生成证书,名字与traefik的certificatesResolvers名称一致
domains:
- main: "*.cuiliangblog.cn" # 不指定的话,默认申请Host域名,可以指定申请通配符域名
然后在阿里云DNS上做解析,重新创建ingress资源时即可触发申请证书。常见错误处理方案
日志关键词 | 原因 | 解决方案 |
---|---|---|
net/http: timeout awaiting response headers或者connect: connection refused | traefik所在节点无法访问Let's Encrypt申请证书 | 使用工具加速 |
acme: error :400/403 | 申请的域名DNS解析记录为配置,或者配置地址不正确,指向了其他IP | 更改DNS解析配置,指向traefik节点所在的公网IP |
acme: error : 429 | 失败次数过多,每个小时只允许请求5次 | 换账号/域名/IP重试或者等一个小时后再试 |
路由配置(IngressRouteTCP)
TCP路由(不带TLS证书)
首先部署一个简单的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:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 6379
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
创建redis应用
[root@k8s-master ingress]# kubectl apply -f redis.yaml
deployment.apps/redis created
service/redis created
创建IngressRouter进行对外暴露
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: redis
spec:
entryPoints:
- tcpep # 指定入口点为tcp端口
routes:
- match: HostSNI(`*`) # 由于Traefik中使用TCP路由配置需要SNI,而SNI又是依赖TLS的,所以我们需要配置证书才行,如果没有证书的话,我们可以使用通配符*(适配ip的)进行配置
services:
- name: redis
port: 6379
查看traefik的dashboard页面是否生效
[root@tiaoban ~]# redis-cli -h redis.test.com -p 9200
redis.test.com:9200> set key_a value_a
OK
redis.test.com:9200> get key_a
"value_a"
redis.test.com:9200>
集群外部客户端配置hosts解析192.168.93.128 redis.test.com
(域名可以随意填写,只要能解析到traefik所在节点即可),然后通过redis-cli工具访问redis,记得指定tcpep的端口。
如果需要再添加其他tcp路由,需要修改traefik配置,新增entryPoints端口。
TCP路由(带TLS证书)
有时候为了安全要求,tcp传输也需要使用TLS证书加密,redis从6.0开始支持了tls证书通信。
[root@k8s-master ingress]# mkdir redis-ssl
[root@k8s-master ingress]# cd redis-ssl/
[root@k8s-master redis-ssl]# openssl genrsa -out ca.key 4096
[root@k8s-master redis-ssl]# openssl req -x509 -new -nodes -sha256 -key ca.key -days 3650 -subj '/O=Redis Test/CN=Certificate Authority' -out ca.crt
[root@k8s-master redis-ssl]# openssl genrsa -out redis.key 2048
[root@k8s-master redis-ssl]# openssl req -new -sha256 -key redis.key -subj '/O=Redis Test/CN=Server' | openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAserial ca.txt -CAcreateserial -days 365 -out redis.crt
oot@k8s-master redis-ssl]# openssl dhparam -out redis.dh 2048
[root@k8s-master redis-ssl]# ll
总用量 24
-rw-r--r-- 1 root root 1895 9月 25 08:34 ca.crt
-rw------- 1 root root 3243 9月 25 08:34 ca.key
-rw-r--r-- 1 root root 41 9月 25 08:35 ca.txt
-rw-r--r-- 1 root root 1407 9月 25 08:35 redis.crt
-rw-r--r-- 1 root root 424 9月 25 08:35 redis.dh
-rw------- 1 root root 1679 9月 25 08:34 redis.key
创建secret资源,使用tls类型,包含redis.crt和redis.key
[root@k8s-master redis-ssl]# kubectl create secret tls redis-tls --key=redis.key --cert=redis.crt
secret/redis-tls created
[root@k8s-master redis-ssl]# kubectl describe secrets redis-tls
Name: redis-tls
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1407 bytes
tls.key: 1679 bytes
创建secret资源,使用generic类型,包含ca.crt
[root@k8s-master redis-ssl]# kubectl create secret generic redis-ca --from-file=ca.crt=ca.crt
secret/redis-ca created
[root@k8s-master redis-ssl]# kubectl describe secrets redis-ca
Name: redis-ca
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
ca.crt: 1895 bytes
修改redis配置,启用tls证书,并挂载证书文件
apiVersion: v1
kind: ConfigMap
metadata:
name: redis
labels:
app: redis
data:
redis.conf : |-
port 0
tls-port 6379
tls-cert-file /etc/tls/tls.crt
tls-key-file /etc/tls/tls.key
tls-ca-cert-file /etc/ca/ca.crt
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 6379
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/redis
- name: tls
mountPath: /etc/tls
- name: ca
mountPath: /etc/ca
args:
- /etc/redis/redis.conf
volumes:
- name: config
configMap:
name: redis
- name: tls
secret:
secretName: redis-tls
- name: ca
secret:
secretName: redis-ca
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
创建资源并查看状态
[root@k8s-master ingress]# kubectl apply -f redis.yaml
configmap/redis created
deployment.apps/redis created
service/redis created
[root@k8s-master ingress]# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-69974c8b56-rzxb6 1/1 Running 0 7s
创建IngressRouter资源,指定域名和证书
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: redis
spec:
entryPoints:
- tcpep
routes:
- match: HostSNI(`redis.test.com`)
services:
- name: redis
port: 6379
tls:
secretName: redis-tls
traefik管理页查看
yum install openssl openssl-devel -y
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make redis-cli BUILD_TLS=yes MALLOC=libc
cp src/redis-cli /usr/local/bin/
yum安装的redis-cli版本为5.0.3,不支持tls,需要编译安装6.0以上版本,并在编译时开启TLS
客户端添加hosts记录192.168.93.128 redis.test.com
,直接访问redis,直接报错
[root@tiaoban src]# ./src/redis-cli -h redis.test.com -p 9200
127.0.0.1:6379> set key 1
Error: Connection reset by peer
客户端使用证书访问redis测试成功
[root@tiaoban src]# ./redis-cli -h redis.test.com -p 9200 --tls --cert /tmp/redis-ssl/redis.crt --key /tmp/redis-ssl/redis.key --cacert /tmp/redis-ssl/ca.crt
redis.test.com:9200> set key 1
OK
路由配置(IngressRouteUDP)
UDP路由
traefik同样也提供了UDP的支持,以我们最常用的rsyslog服务为例,演示traefik如果配置使用 首先制作一个rsyslog镜像
[root@k8s-master udp]# ls
Dockerfile rsyslog.conf
# rsyslog配置
[root@k8s-master udp]# cat rsyslog.conf
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
$ModLoad imudp
$UDPServerRun 514
$WorkDirectory /var/lib/rsyslog
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$IncludeConfig /etc/rsyslog.d/*.conf
$OmitLocalLogging off
*.info;mail.none;authpriv.none;cron.none /var/log/messages
authpriv.* /var/log/secure
mail.* -/var/log/maillog
cron.* /var/log/cron
*.emerg :omusrmsg:*
uucp,news.crit /var/log/spooler
local7.* /var/log/boot.log
# dockerfile配置
[root@k8s-master udp]# cat Dockerfile
FROM centos:7
RUN yum -y install rsyslog && rm -rf /etc/rsyslog.d/listen.conf
COPY rsyslog.conf /etc/rsyslog.conf
EXPOSE 514/udp
CMD ["/usr/sbin/rsyslogd", "-dn"]
# 构建镜像
[root@k8s-master udp]# docker build -t rsyslog:v1 .
接下来创建rsyslog的资源清单
[root@k8s-master udp]# cat rsyslog-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rsyslog
spec:
selector:
matchLabels:
app: rsyslog
template:
metadata:
labels:
app: rsyslog
spec:
containers:
- name: rsyslog
image: rsyslog:v1
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 514
protocol: UDP
---
apiVersion: v1
kind: Service
metadata:
name: rsyslog
spec:
selector:
app: rsyslog
ports:
- port: 514
protocol: UDP
targetPort: 514
部署上面的应用并查看
[root@k8s-master udp]# kubectl apply -f rsyslog-deployment.yaml
deployment.apps/rsyslog created
service/rsyslog created
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
rsyslog-5dfc9d64b5-d9wjj 1/1 Running 0 3s
创建IngressRouter资源,代理UDP应用,需要注意的是UDP资源访问时直接通过公网ip+dup的entryPoints端口即可,不需要配置域名
[root@k8s-master udp]# cat rsyslog-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: rsyslog
spec:
entryPoints:
- udpep
routes:
- services:
- name: rsyslog
port: 514
[root@k8s-master udp]# kubectl apply -f rsyslog-ingress.yaml
ingressrouteudp.traefik.containo.us/rsyslog created
查看dashboard的udp信息
集群外部访问udp服务,通过 Traefik 所在节点的公网 IP(192.168.93.128)与 entryPoints端口(9300)来访问 UDP 应用进行测试
[root@tiaoban ~]# logger -n 192.168.93.128 -P 9300 "cuiliang123"
[root@tiaoban ~]# logger -n 192.168.93.128 -P 9300 "hello 123"
查看rsyslog日志,验证请求是否成功
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
rsyslog-5dfc9d64b5-d9wjj 1/1 Running 0 6m48s
[root@k8s-master ~]# kubectl exec -it rsyslog-5dfc9d64b5-d9wjj -- bash
[root@rsyslog-5dfc9d64b5-d9wjj /]# tail -n 5 /var/log/messages
Sep 25 04:06:20 rsyslog-5dfc9d64b5-d9wjj rsyslogd: [origin software="rsyslogd" swVersion="8.24.0-57.el7_9.3" x-pid="1" x-info="http://www.rsyslog.com"] start
Sep 25 12:12:55 tiaoban root cuiliang123
Sep 25 12:17:19 tiaoban root hello 123
负载均衡配置
traefik可以对http、TCP、UDP实现负载均衡,根据需求创建IngressRoute/IngressRouteTCP/IngressRouteUDP即可,此处以http为例。
http路由多个k8s service配置
创建两个deployment应用与对应的svc
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp1-795d947b45-9lsm6 1/1 Running 1 25h
myapp2-6ffd54f76-ljkr9 1/1 Running 1 25h
[root@k8s-master udp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 2d22h
myapp1 ClusterIP 10.104.91.200 <none> 80/TCP 25h
myapp2 ClusterIP 10.111.245.32 <none> 80/TCP
创建IngressRouter资源,配置域名为myapp.test.com,请求流量均摊到两个k8s的service上。
[root@k8s-master ingress]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`)
kind: Rule
services:
- name: myapp1
namespace: default
port: 80
- name: myapp2
namespace: default
port: 80
[root@k8s-master ingress]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp created
查看dashboard页面路由信息,发现已成功配置代理两个service服务,且权重均为1
解析来访问测试,发现依次循环响应myapp1和myapp2的内容
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
三、入门Traefik系列------中间件
简介
Traefik Middlewares 是一个处于路由和后端服务之前的中间件,在外部流量进入 Traefik,且路由规则匹配成功后,将流量发送到对应的后端服务前,先将其发给中间件进行一系列处理(类似于过滤器链 Filter,进行一系列处理),例如,添加 Header 头信息、鉴权、流量转发、处理访问路径前缀、IP 白名单等等,经过一个或者多个中间件处理完成后,再发送给后端服务,这个就是中间件的作用。Traefik内置了很多不同功能的Middleware,主要是针对HTTP和TCP,这里挑选几个比较常用的进行演示。
参考文档:https://doc.traefik.io/traefik/middlewares/overview/
重定向
redirectScheme的更多用法参考文档https://doc.traefik.io/traefik/middlewares/http/redirectscheme/
还是以前面的deployment应用与对应的svc为例
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp2-795d947b45-9lsm6 1/1 Running 1 25h
[root@k8s-master udp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 2d22h
myapp2 ClusterIP 10.104.91.200 <none> 80/TCP
[root@k8s-master ingress]# kubectl describe secrets myapp2-tls
Name: myapp2-tls
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1131 bytes
tls.key: 1704 bytes
创建一个https的IngressRoute
[root@k8s-master middleware]# cat https-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2-tls
spec:
entryPoints:
- websecure
routes:
- match: Host(`myapp2.test.com`)
kind: Rule
services:
- name: myapp2
port: 80
tls:
secretName: myapp2-tls # 指定tls证书名称
[root@k8s-master middleware]# kubectl apply -f https-ingress.yaml
ingressroute.traefik.containo.us/myapp2-tls created
定义一个强制将http请求跳转到https的中间件。
[root@k8s-master middleware]# cat https-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https-middleware
spec:
redirectScheme:
scheme: https
[root@k8s-master middleware]# kubectl apply -f https-middleware.yaml
middleware.traefik.containo.us/redirect-https-middleware created
定义一个http的IngressRoute,并使用中间件
[root@k8s-master middleware]# cat http-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2
spec:
entryPoints:
- web
routes:
- match: Host(`myapp2.test.com`)
kind: Rule
services:
- name: myapp2
port: 80
middlewares:
- name: redirect-https-middleware # 指定使用RedirectScheme中间件,完成http强制跳转至https
[root@k8s-master middleware]# kubectl apply -f http-ingress.yaml
ingressroute.traefik.containo.us/myapp1 created
访问测试,当用户访问http://myapp.test.com
时会强制跳转到https://myapp.test.com
去除请求路径前缀
假设现在有这样一个需求,当访问http://myapp.test.com/v1
时,流量调度至myapp1。当访问http://myapp.test.com/v2
时,流量调度至myapp2。这种需求是非常常见的,在NGINX中,我们可以配置多个Location来定制规则,使用Traefik也可以这么做。但是定制不同的前缀后,由于应用本身并没有这些前缀,导致请求返回404,这时候我们就需要对请求的path进行处理。
参考文档https://doc.traefik.io/traefik/middlewares/http/stripprefix/
创建一个IngressRoute,并设置两条规则,根据不同的访问路径代理至相对应的service
[root@k8s-master middleware]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`) && PathPrefix(`/v1`)
kind: Rule
services:
- name: myapp1
port: 80
- match: Host(`myapp.test.com`) && PathPrefix(`/v2`)
kind: Rule
services:
- name: myapp2
port: 80
[root@k8s-master middleware]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp created
进行访问测试http://myapp.test.com/v1
,虽然traefik配置无误,但是由于myapp1应用并没有v1这个路径,因此返回404页面
接下来定义去除前缀的中间件stripPrefix,指定将请求路径中的v1、v2去除。
[root@k8s-master middleware]# cat prefix-url-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: prefix-url-middleware
spec:
stripPrefix:
prefixes:
- /v1
- /v2
[root@k8s-master middleware]# kubectl apply -f prefix-url-middleware.yaml
middleware.traefik.containo.us/prefix-url-middleware created
修改上面的ingressRoute,添加刚刚定义的prefix-url-middleware中间件
[root@k8s-master middleware]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`) && PathPrefix(`/v1`)
kind: Rule
services:
- name: myapp1
port: 80
middlewares:
- name: prefix-url-middleware
- match: Host(`myapp.test.com`) && PathPrefix(`/v2`)
kind: Rule
services:
- name: myapp2
port: 80
middlewares:
- name: prefix-url-middleware
[root@k8s-master middleware]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp configured
查看traefik的dashboard,已添加了中间件
接下来进行访问测试
添加IP白名单
为提高安全性,通常情况下一些管理员界面会设置ip访问白名单,只希望个别用户可以访问,例如访问traefik的dashboard的url,这时候就可以使用Traefik中的ipWhiteList中间件来完成。
参考文档https://doc.traefik.io/traefik/middlewares/http/ipwhitelist/
当前traefik的dashboard任何主机都可以访问
[root@tiaoban ~]# curl http://traefik.test.com/dashboard/
<!DOCTYPE html><html><head><title>Traefik</title><meta charset=utf-8><meta name=description content="Traefik UI">......
接下来定义IP访问白名单的中间件ipWhiteList,指定可以访问的ip列表。
[root@k8s-master middleware]# cat ip-white-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: ip-white-list-middleware
spec:
ipWhiteList:
sourceRange:
- 127.0.0.1/32
- 192.168.93.1
[root@k8s-master middleware]# kubectl apply -f ip-white-middleware.yaml
middleware.traefik.containo.us/ip-white-list-middleware created
修改dashboard的ingressRoute,添加ip白名单中间件
[root@k8s-master middleware]# cat dashboard-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
middlewares:
- name: ip-white-list-middleware
[root@k8s-master middleware]# kubectl apply -f dashboard-ingress.yaml
ingressroute.traefik.containo.us/dashboard configured
接下来使用白名单之外的ip访问测试
[root@tiaoban ~]# curl traefik.test.com/dashboard/
Forbidden[root@tiaoban ~]#
[root@tiaoban ~]# curl -I http://traefik.test.com/dashboard/
HTTP/1.1 403 Forbidden
Date: Sun, 25 Sep 2022 13:32:49 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8
基础用户认证
通常企业安全要求规范除了要对管理员页面限制访问ip外,还需要添加账号密码认证,而traefik默认没有提供账号密码认证功能,此时就可以通过BasicAuth中间件完成用户认证,只有认证通过的授权用户才可以访问页面。
参考文档:https://doc.traefik.io/traefik/middlewares/http/basicauth/
使用basicAuth认证需要使用htpasswd工具生成密码文件,因此先安装httpd软件包
[root@k8s-master middleware]# dnf install -y httpd
使用htpasswd工具设置用户名密码,生成密钥文件
[root@k8s-master middleware]# htpasswd -bc basic-auth-secret cuiliang 123
Adding password for user cuiliang
将生成的basic-auth-secret密码文件创建成secret资源
[root@k8s-master middleware]# kubectl create secret generic basic-auth --from-file=basic-auth-secret
secret/basic-auth created
[root@k8s-master middleware]# kubectl get secrets
NAME TYPE DATA AGE
basic-auth Opaque 1 8s
接下来创建basicAuth中间件,指定使用刚刚创建的secret资源。
[root@k8s-master middleware]# cat basic-auth-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: basic-auth-middleware
spec:
basicAuth:
secret: basic-auth
[root@k8s-master middleware]# kubectl apply -f basic-auth-middleware.yaml
middleware.traefik.containo.us/basic-auth-middleware created
修改dashboard的ingressRoute,添加basicAuth中间件
[root@k8s-master middleware]# cat basic-auth-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: basic-auth-middleware
spec:
basicAuth:
secret: basic-auth
[root@k8s-master middleware]# cat dashboard-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
middlewares:
- name: basic-auth-middleware
[root@k8s-master middleware]# kubectl apply -f basic-auth-middleware.yaml
middleware.traefik.containo.us/basic-auth-middleware created
客户端访问验证,刷新页面后,弹出用户登录认证页面。
修改请求/响应头信息
为了提高业务的安全性,安全团队会定期进行漏洞扫描,其中有些web漏洞就需要通过修改响应头处理,traefik的Headers中间件不仅可以修改返回客户端的响应头信息,还能修改反向代理后端service服务的请求头信息。
例如对https://myapp2.test.com
提高安全策略,强制启用HSTS HSTS:即HTTP严格传输安全响应头,收到该响应头的浏览器会在 63072000s(约 2 年)的时间内,只要访问该网站,即使输入的是 http,浏览器会自动跳转到 https。(HSTS 是浏览器端的跳转,之前的HTTP 重定向到 HTTPS是服务器端的跳转)
参考文档https://doc.traefik.io/traefik/middlewares/http/headers/
定义响应头中间件Headers,指定响应内容中添加Strict-Transport-Security配置。
[root@k8s-master middleware]# cat hsts-header-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: hsts-header-middleware
spec:
headers:
customResponseHeaders:
Strict-Transport-Security: 'max-age=63072000'
[root@k8s-master middleware]# kubectl apply -f hsts-header-middleware.yaml
middleware.traefik.containo.us/hsts-header-middleware created
修改myapp2的ingressRoute,添加headers中间件
[root@k8s-master middleware]# cat myapp2-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp2-tls
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`myapp2.test.com`)
kind: Rule
services:
- name: myapp2
port: 80
middlewares:
- name: hsts-header-middleware
tls:
secretName: myapp2-tls # 指定tls证书名称
[root@k8s-master middleware]# kubectl apply -f myapp2-ingress.yaml
ingressroute.traefik.containo.us/myapp2-tls configured
客户端访问验证,查看响应头信息
限流
在实际生产环境中,流量限制也是经常用到的,它可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御 DDOS 攻击。更常见的情况,该功能被用来保护下游应用服务器不被同时太多用户请求所压垮。
参考文档https://doc.traefik.io/traefik/middlewares/http/ratelimit/
先模拟正常情况,无任何限流措施,对myapp1使用ab工具进行压力测试,一共请求一百次,每次并发10次。测试结果失败的请求为0,总耗时0.412秒
[root@tiaoban ~]# ab -n 100 -c 10 "http://myapp1.test.com/"
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking myapp1.test.com (be patient).....done
Server Software: nginx/1.12.2
Server Hostname: myapp1.test.com
Server Port: 80
Document Path: /
Document Length: 65 bytes
Concurrency Level: 10
Time taken for tests: 0.412 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 27700 bytes
HTML transferred: 6500 bytes
Requests per second: 242.78 [#/sec] (mean)
Time per request: 41.189 [ms] (mean)
Time per request: 4.119 [ms] (mean, across all concurrent requests)
Transfer rate: 65.68 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 4 4.3 3 16
Processing: 4 20 19.2 15 173
Waiting: 4 20 19.0 14 171
Total: 8 25 19.5 19 175
Percentage of the requests served within a certain time (ms)
50% 19
66% 27
75% 30
80% 32
90% 43
95% 55
98% 61
99% 175
100% 175 (longest request)
定义限流中间件RateLimit,指定1s内请求数平均值不大于10个,高峰最大值不大于50个。
[root@k8s-master middleware]# cat rate-limit-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: rate-limit-middleware
spec:
rateLimit:
burst: 10
average: 50
[root@k8s-master middleware]# kubectl apply -f rate-limit-middleware.yaml
middleware.traefik.containo.us/rate-limit-middleware created
修改myapp1的ingressRoute,添加RateLimit中间件
[root@k8s-master middleware]# cat myapp1-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp1
spec:
entryPoints:
- web
routes:
- match: Host(`myapp1.test.com`)
kind: Rule
services:
- name: myapp1
port: 80
middlewares:
- name: rate-limit-middleware
[root@k8s-master middleware]# kubectl apply -f myapp1-ingress.yaml
ingressroute.traefik.containo.us/myapp1 created
接下来继续使用ab工具进行压力测试,一共请求一百次,每次并发10次。测试结果失败的请求为82次,总耗时0.297秒
[root@tiaoban ~]# ab -n 100 -c 10 "http://myapp1.test.com/"
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking myapp1.test.com (be patient).....done
Server Software: nginx/1.12.2
Server Hostname: myapp1.test.com
Server Port: 80
Document Path: /
Document Length: 65 bytes
Concurrency Level: 10
Time taken for tests: 0.297 seconds
Complete requests: 100
Failed requests: 82
(Connect: 0, Receive: 0, Length: 82, Exceptions: 0)
Non-2xx responses: 82
Total transferred: 20562 bytes
HTML transferred: 2564 bytes
Requests per second: 336.30 [#/sec] (mean)
Time per request: 29.736 [ms] (mean)
Time per request: 2.974 [ms] (mean, across all concurrent requests)
Transfer rate: 67.53 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 3 3.4 2 17
Processing: 2 12 15.9 9 151
Waiting: 2 11 15.7 7 150
Total: 3 15 16.2 11 153
Percentage of the requests served within a certain time (ms)
50% 11
66% 14
75% 18
80% 21
90% 24
95% 34
98% 49
99% 153
100% 153 (longest request)
熔断
熔断简介 服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。熔断器三种状态
-
Closed:关闭状态,所有请求都正常访问。
-
Open:打开状态,所有请求都会被降级。traefik会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。
-
Recovering:半开恢复状态,open状态不是永久的,打开后会进入休眠时间。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时
服务熔断原理(断路器的原理) 统计用户在指定的时间范围(默认10s)之内的请求总数达到指定的数量之后,如果不健康的请求(超时、异常)占总请求数量的百分比(50%)达到了指定的阈值之后,就会触发熔断。触发熔断,断路器就会打开(open),此时所有请求都不能通过。在5s之后,断路器会恢复到半开状态(half open),会允许少量请求通过,如果这些请求都是健康的,那么断路器会回到关闭状态(close).如果这些请求还是失败的请求,断路器还是恢复到打开的状态(open).traefik支持的触发器
-
NetworkErrorRatio:网络错误率
-
ResponseCodeRatio:状态代码比率
-
LatencyAtQuantileMS:分位数的延迟(以毫秒为单位)
参考文档https://doc.traefik.io/traefik/middlewares/http/circuitbreaker/
定义熔断中间件circuitBreaker,指定50% 的请求比例响应时间大于 1MS 时熔断。
[root@k8s-master middleware]# cat circuit-breaker-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: circuit-breaker-middleware
spec:
circuitBreaker:
expression: LatencyAtQuantileMS(50.0) > 1
[root@k8s-master middleware]# kubectl apply -f circuit-breaker-middleware.yaml
middleware.traefik.containo.us/circuit-breaker-middleware created
修改myapp1的ingressRoute,添加circuitBreaker中间件
root@k8s-master middleware]# cat myapp1-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp1
spec:
entryPoints:
- web
routes:
- match: Host(`myapp1.test.com`)
kind: Rule
services:
- name: myapp1
port: 80
middlewares:
- name: circuit-breaker-middleware
root@k8s-master middleware]# kubectl apply -f myapp1-ingress.yaml
ingressroute.traefik.containo.us/myapp1 created
继续进行压力测试,一共请求一千次,每次并发100次。触发熔断机制,测试结果失败的请求为999次,总耗时0.938秒。
[root@tiaoban ~]# ab -n 1000 -c 100 "http://myapp1.test.com/"
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking myapp1.test.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: nginx/1.12.2
Server Hostname: myapp1.test.com
Server Port: 80
Document Path: /
Document Length: 65 bytes
Concurrency Level: 100
Time taken for tests: 0.938 seconds
Complete requests: 1000
Failed requests: 999
(Connect: 0, Receive: 0, Length: 999, Exceptions: 0)
Non-2xx responses: 999
Total transferred: 153124 bytes
HTML transferred: 19046 bytes
Requests per second: 1065.54 [#/sec] (mean)
Time per request: 93.849 [ms] (mean)
Time per request: 0.938 [ms] (mean, across all concurrent requests)
Transfer rate: 159.34 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 12 9.2 11 58
Processing: 2 66 53.3 53 314
Waiting: 1 63 51.7 51 309
Total: 5 79 52.8 66 326
Percentage of the requests served within a certain time (ms)
50% 66
66% 80
75% 96
80% 105
90% 128
95% 168
98% 280
99% 309
100% 326 (longest request)
自定义错误页
在实际的业务中,肯定会存在4XX 5XX相关的错误异常,如果每个应用都开发一个单独的错误页,无疑大大增加了开发成本,traefik同样也支持自定义错误页,但是需要注意的是,错误页面不是有traefik存储处理,而是通过定义中间件,将错误的请求重定向到其他的页面。
参考文档:https://doc.traefik.io/traefik/middlewares/http/errorpages/
首先,我们先创建一个应用,使用flask开个一个简单的demo项目。这个web应用的功能是:当请求/时,返回状态码为200,当请求/400时,返回400状态码,当请求/500时,返回500状态码。应用的源代码如下:
-
from flask import Flask, abort app = Flask(__name__) @app.route('/') def hello_world():# put application's code here return'Hello World!' @app.route('/400') def error_404(): abort(400) @app.route('/500') def error_500(): abort(500) if __name__ == '__main__': app.run()
-
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>flask</title> </head> <body> <h1>hello flask</h1> <img src="{{ url_for('static',filename='photo.jpg') }}" alt="photo"> </body> </html>
为了方便大家测试,已将镜像打包上传至docker hub仓库
docker pull cuiliang0302/request-code:v2.0
接下来我们使用deployment控制器部署这个服务,并创建svc资源
[root@k8s-master middleware]# cat flask.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask
spec:
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: cuiliang0302/request-code:v1.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: flask
spec:
type: ClusterIP
selector:
app: flask
ports:
- port: 5000
targetPort: 5000
[root@k8s-master middleware]# kubectl apply -f flask.yaml
deployment.apps/flask created
service/flask created
接下来创建ingressRouter资源
[root@k8s-master middleware]# cat flask-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: flask
spec:
entryPoints:
- web
routes:
- match: Host(`flask.test.com`)
kind: Rule
services:
- name: flask
port: 5000
[root@k8s-master middleware]# kubectl apply -f flask-ingress.yaml
ingressroute.traefik.containo.us/flask created
使用域名访问验证,先添加hosts解析记录192.168.93.128 flask.test.com
,分别请求不同的路径,模拟4XX 5XX错误
[root@k8s-master middleware]# curl -I flask.test.com/
HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/html; charset=utf-8
Date: Wed, 28 Sep 2022 03:11:03 GMT
Server: Werkzeug/2.2.2 Python/3.10.1
[root@k8s-master middleware]# curl -I flask.test.com/400
HTTP/1.1 400 Bad Request
Content-Length: 167
Content-Type: text/html; charset=utf-8
Date: Wed, 28 Sep 2022 03:11:07 GMT
Server: Werkzeug/2.2.2 Python/3.10.1
[root@k8s-master middleware]# curl -I flask.test.com/500
HTTP/1.1 500 Internal Server Error
Content-Length: 265
Content-Type: text/html; charset=utf-8
Date: Wed, 28 Sep 2022 03:11:11 GMT
Server: Werkzeug/2.2.2 Python/3.10.1
[root@k8s-master middleware]# curl -I flask.test.com/404
HTTP/1.1 404 Not Found
Content-Length: 207
Content-Type: text/html; charset=utf-8
Date: Wed, 28 Sep 2022 03:11:17 GMT
Server: Werkzeug/2.2.2 Python/3.10.1
现在提出一个新的需求,当我访问flask项目时,如果错误码为400,返回myapp1的页面,如果错误码为500,返回myapp2的页面(前提是myapp1和myapp2服务已创建)。我们创建errorpages中间件
[root@k8s-master middleware]# cat error-middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: errors5
spec:
errors:
status:
- "500-599"
# query: /{status}.html # 可以为每个页面定义一个状态码,也可以指定5XX使用统一页面返回
query : / # 指定返回myapp2的请求路径
service:
name: myapp2
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: errors4
spec:
errors:
status:
- "400-499"
# query: /{status}.html # 可以为每个页面定义一个状态码,也可以指定5XX使用统一页面返回
query : / # 指定返回myapp1的请求路径
service:
name: myapp1
port: 80
[root@k8s-master middleware]# kubectl apply -f error-middleware.yaml
middleware.traefik.containo.us/errors5 created
middleware.traefik.containo.us/errors4 created
接下来修改ingressroute资源,添加错误码中间件
[root@k8s-master middleware]# cat flask-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: flask
spec:
entryPoints:
- web
routes:
- match: Host(`flask.test.com`)
kind: Rule
services:
- name: flask
port: 5000
middlewares:
- name: errors4
- name: errors5
[root@k8s-master middleware]# kubectl apply -f error-middleware.yaml
middleware.traefik.containo.us/errors5 created
middleware.traefik.containo.us/errors4 created
最后进行访问验证
# 测试200状态码
[root@k8s-master middleware]# curl -I flask.test.com/
HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/html; charset=utf-8
Date: Thu, 06 Oct 2022 00:36:19 GMT
Server: Werkzeug/2.2.2 Python/3.10.1
[root@k8s-master middleware]# curl flask.test.com/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flask</title>
</head>
<body>
<h1>hello flask</h1>
<img src="/static/photo.jpg" alt="photo">
</body>
# 测试400状态码
[root@k8s-master middleware]# curl -I flask.test.com/400
HTTP/1.1 400 Bad Request
Accept-Ranges: bytes
Content-Length: 65
Content-Type: text/html
Date: Thu, 06 Oct 2022 00:36:35 GMT
Etag: "5a98c760-41"
Last-Modified: Fri, 02 Mar 2018 03:39:12 GMT
Server: nginx/1.12.2
[root@k8s-master middleware]# curl flask.test.com/400
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
# 测试500状态码
[root@k8s-master middleware]# curl -I flask.test.com/500
HTTP/1.1 500 Internal Server Error
Accept-Ranges: bytes
Content-Length: 65
Content-Type: text/html
Date: Thu, 06 Oct 2022 00:36:46 GMT
Etag: "5a9251f0-41"
Last-Modified: Sun, 25 Feb 2018 06:04:32 GMT
Server: nginx/1.12.2
[root@k8s-master middleware]# curl flask.test.com/500
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# 测试404状态码
[root@k8s-master middleware]# curl -I flask.test.com/404
HTTP/1.1 404 Not Found
Accept-Ranges: bytes
Content-Length: 65
Content-Type: text/html
Date: Thu, 06 Oct 2022 00:37:00 GMT
Etag: "5a98c760-41"
Last-Modified: Fri, 02 Mar 2018 03:39:12 GMT
Server: nginx/1.12.2
[root@k8s-master middleware]# curl flask.test.com/404
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
数据压缩
有时候客户端和服务器之间会传输比较大的报文数据,这时候就占用较大的网络带宽和时长。为了节省带宽,加速报文的响应速速,可以将传输的报文数据先进行压缩,然后再进行传输,traefik也同样支持数据压缩。
参考文档:https://doc.traefik.io/traefik/middlewares/http/compress/\[10\]
traefik默认只对大于1024字节,且请求标头包含Accept-Encoding gzip
的资源进行压缩。可以指定排除特定类型不启用压缩或者根据内容大小来决定是否压缩。继续使用上面创建的flask应用,现在创建中间件,使用默认配置策略即可。
[root@k8s-master middleware]# cat compress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: compress
spec:
compress: {}
[root@k8s-master middleware]# kubectl apply -f compress.yaml
middleware.traefik.containo.us/compress created
修改flask的ingressrouter资源,指定数据压缩中间件
[root@k8s-master middleware]# cat flask-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: flask
spec:
entryPoints:
- web
routes:
- match: Host(`flask.test.com`)
kind: Rule
services:
- name: flask
port: 5000
middlewares:
- name: compress
[root@k8s-master middleware]# kubectl apply -f flask-ingress.yaml
ingressroute.traefik.containo.us/flask created
接下来查看浏览器f12调试信息
图片资源大于1024字节,开启了压缩
html资源小于1024字节,未启用压缩
四、入门Traefik系列------服务配置与使用
简介
traefik的路由规则就可以实现4层和7层的基本负载均衡操作,使用IngressRoute[1]/IngressRouteTCP[2]/IngressRouteUDP[3]资源即可。但是如果想要实现加权轮询、流量复制等高级操作,traefik抽象出了一个TraefikService[4]资源。此时整体流量走向为:外部流量先通过entryPoints端口进入traefik,然后由IngressRoute[5]/IngressRouteTCP[6]/IngressRouteUDP[7]匹配后进入TraefikService[8],在TraefikService[9]这一层实现加权轮循和流量复制,最后将请求转发至kubernetes的service。除此之外traefik还支持7层的粘性会话、健康检查、传递请求头、响应转发、故障转移等操作。
加权轮询(灰度发布)
灰度发布我们有时候也会称为金丝雀发布(Canary),主要就是让一部分测试的服务也参与到线上去,经过测试观察看是否符合上线要求,在traefik中,通过调整生产与测试服务的权重,实现灰度发布的功能。接下来配置加权轮循,仍然请求myapp.test.com,myapp1的负载权重为1,myapp2的负载权重为2。依旧是上面创建的两个deployment应用与对应的svc
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp1-795d947b45-9lsm6 1/1 Running 1 25h
myapp2-6ffd54f76-ljkr9 1/1 Running 1 25h
[root@k8s-master udp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 2d22h
myapp1 ClusterIP 10.104.91.200 <none> 80/TCP 25h
myapp2 ClusterIP 10.111.245.32 <none> 80/TCP
创建IngressRouter资源,配置域名为myapp.test.com,注意此时后端service配置TraefikService。
[root@k8s-master ingress]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`)
kind: Rule
services: # 加权轮循时,后端service不再是k8s的service,而是traefik的TraefikService
- name: wrr
namespace: default
kind: TraefikService
[root@k8s-master ingress]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp created
创建TraefikService资源,名称与IngressRouter的TraefikService保持一致,services后端填写kubernetes的service,并指定权重。
[root@k8s-master ingress]# cat myapp-traefikService.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: wrr
namespace: default
spec:
weighted:
services:
- name: myapp1
port: 80
weight: 1
- name: myapp2
port: 80
weight: 2
[root@k8s-master ingress]# kubectl apply -f myapp-traefikService.yaml
traefikservice.traefik.containo.us/wrr unchanged
查看dashboard的配置信息,此时myapp2权重为2,myapp1权重为1。
客户端访问测试,验证无误。
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
镜像复制(流量复制)
traefik还支持镜像复制功能,是一种可以将流入流量复制并同时将其发送给其他服务的方法,镜像服务可以获得给定百分比的请求同时也会忽略这部分请求的响应,在实际生产中主要用于测试场景以及问题复现bug定位。依旧是上面创建的两个deployment应用与对应的svc
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp1-795d947b45-9lsm6 1/1 Running 1 25h
myapp2-6ffd54f76-ljkr9 1/1 Running 1 25h
[root@k8s-master udp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 2d22h
myapp1 ClusterIP 10.104.91.200 <none> 80/TCP 25h
myapp2 ClusterIP 10.111.245.32 <none> 80/TCP
创建IngressRouter资源,配置域名为myapp.test.com,注意此时后端service配置TraefikService。
[root@k8s-master ingress]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`)
kind: Rule
services: # 流量复制时,后端service不再是k8s的service,而是traefik的TraefikService
- name: mirror
namespace: default
kind: TraefikService
[root@k8s-master ingress]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp created
创建TraefikService资源,名称与IngressRouter的TraefikService保持一致,services后端填写kubernetes的service,并设置复制流量比例。
[root@k8s-master ingress]# cat myapp-traefikService.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: mirror
namespace: default
spec:
mirroring: # 所有流量全部请求到k8s的myapp1
name: myapp1
port: 80
mirrors: # 同时复制50%的请求到k8s的myapp2
- name: myapp2
port: 80
percent: 50
[root@k8s-master ingress]# kubectl apply -f myapp-traefikService.yaml
traefikservice.traefik.containo.us/mirror created
查看dashboard的配置信息,此时myapp2类型为mirroring,比例为50%
客户端访问测试,只响应了myapp1的内容。
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
查看myapp1和myapp2的日志,发现有请求日志符合预期。
[root@k8s-master ingress]# kubectl logs myapp1-795d947b45-9lsm6
10.244.0.0 - - [25/Sep/2022:08:45:33 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
10.244.0.0 - - [25/Sep/2022:08:45:35 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
10.244.0.0 - - [25/Sep/2022:08:45:36 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
10.244.0.0 - - [25/Sep/2022:09:45:38 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
[root@k8s-master ingress]# kubectl logs myapp2-6ffd54f76-ljkr9
10.244.0.0 - - [25/Sep/2022:08:45:33 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
10.244.0.0 - - [25/Sep/2022:08:45:36 +0000] "GET / HTTP/1.1" 200 65 "-""curl/7.61.1""192.168.93.1"
粘性会话(会话保持)
当我们使用traefik的负载均衡时,默认情况下轮循多个k8s的service服务,如果用户对同一内容的多次请求,可能被转发到了不同的后端服务器。假设用户发出请求被分配至服务器A,保存了一些信息在session中,该用户再次发送请求被分配到服务器B,要用之前保存的信息,若服务器A和B之间没有session粘滞,那么服务器B就拿不到之前的信息,这样会导致一些问题。traefik同样也支持粘性会话,可以让用户在一次会话周期内的所有请求始终转发到一台特定的后端服务器上。依旧是上面创建的两个deployment应用与对应的svc
[root@k8s-master udp]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp1-795d947b45-9lsm6 1/1 Running 1 25h
myapp2-6ffd54f76-ljkr9 1/1 Running 1 25h
[root@k8s-master udp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 2d22h
myapp1 ClusterIP 10.104.91.200 <none> 80/TCP 25h
myapp2 ClusterIP 10.111.245.32 <none> 80/TCP
创建IngressRouter资源,配置域名为myapp.test.com,注意此时后端service配置TraefikService。
[root@k8s-master ingress]# cat myapp-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`myapp.test.com`)
kind: Rule
services: # 粘性会话依赖加权轮循,后端service不再是k8s的service,而是traefik的TraefikService
- name: wrr
namespace: default
kind: TraefikService
[root@k8s-master ingress]# kubectl apply -f myapp-ingress.yaml
ingressroute.traefik.containo.us/myapp created
创建TraefikService资源,名称与IngressRouter的TraefikService保持一致,services后端填写kubernetes的service,并指定权重。
[root@k8s-master ingress]# cat myapp-traefikService.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: wrr
namespace: default
spec:
weighted:
services:
- name: myapp1
kind: Service
port: 80
weight: 1
- name: myapp2
kind: Service
weight: 2
port: 80
sticky: # 开启粘性会话
cookie: # 基于cookie区分客户端
name: lvl1 # 指定客户端请求时,包含的cookie名称
[root@k8s-master ingress]# kubectl apply -f myapp-traefikService.yaml
traefikservice.traefik.containo.us/wrr unchanged
客户端携带cookie信息访问测试。
# lvl1为default-myapp2-80的请求全部由myapp2响应
[root@tiaoban ~]# curl -b "lvl1=default-myapp2-80" http://myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl -b "lvl1=default-myapp2-80" http://myapp.test.com
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# lvl1为default-myapp1-80的请求全部由myapp1响应
[root@tiaoban ~]# curl -b "lvl1=default-myapp1-80" http://myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@tiaoban ~]# curl -b "lvl1=default-myapp1-80" http://myapp.test.com
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>