ingressRoute简介 kubernetes 中使用 Traefik ingress 的 ingressRoute 代理 http
、https
、tcp
、udp
。
官方文档
三种方式 Traefik 创建路由规则有多种方式,比如:
原生 Ingress
写法 使用 CRD IngressRoute
方式 使用 GatewayAPI
的方式 相较于原生 Ingress 写法,ingressRoute 是 2.1 以后新增功能,简单来说,他们都支持路径 (path) 路由和域名 (host) HTTP 路由,以及 HTTPS 配置,区别在于 IngressRoute 需要定义 CRD 扩展,但是它支持了 TCP、UDP 路由以及中间件等新特性,强烈推荐使用 ingressRoute
匹配规则 规则 描述 Headers(key
, value
) 检查headers中是否有一个键为key值为value的键值对 HeadersRegexp(key
, regexp
) 检查headers中是否有一个键位key值为正则表达式匹配的键值对 Host(example.com
, boysec.cn
, …) 检查请求的域名是否包含在特定的域名中 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 和网段格式。
通过dashboard演示 如下所示通过 Ingress
创建一个资源对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: traefik-dashboard namespace: kube-system annotations: kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: web spec: rules: - host: ingress.od.com http: paths: - pathType: Prefix path: / backend: service: name: traefik-v2 port: number: 8090
访问: http://ingress.od.com
如下所示通过 ingressRoute
创建一个资源对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: traefik-dashboard namespace: kube-system spec: entryPoints: - web routes: - match: Host(`traefik.od.com`) kind: Rule services: - name: api@internal kind: TraefikService
访问:http://traefik.od.com
ingressRoute http路由 实现目标:集群外部用户通过访问http://whoami.od.com 域名时,将请求代理至whoami应用。
创建如下所示的 whoami
资源配置清单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 cat > whoami.yml <<EOF apiVersion: v1 kind: Service metadata: name: whoami spec: ports: - protocol: TCP name: web port: 80 selector: app: whoami --- kind: Deployment apiVersion: apps/v1 metadata: name: whoami labels: app: whoami spec: replicas: 1 selector: matchLabels: app: whoami template: metadata: labels: app: whoami spec: containers: - name: whoami image: containous/whoami ports: - name: web containerPort: 80 EOF
定义一个 IngressRoute 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cat > who-ing.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingressroute-demo spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 EOF
通过 entryPoints
指定了我们这个应用的入口点是 web
,也就是通过 80 端口访问,然后访问的规则就是要匹配 whoami.od.com
这个域名,并且具有 /notls
的路径前缀的请求才会被 whoami
这个 Service 所匹配。我们可以直接创建上面的几个资源对象,然后对域名做对应的解析后,就可以访问应用:http://whoami.od.com/notls
https 路由 如果我们需要用 HTTPS 来访问我们这个应用的话,就需要监听 websecure
这个入口点,也就是通过 443 端口来访问,同样用 HTTPS 访问应用必然就需要证书,这里我们用 openssl
来创建一个自签名的证书
1 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=whoami.od.com"
然后通过 Secret 对象来引用证书文件:
1 2 # 要注意证书文件名称必须是 tls.crt 和 tls.key kubectl create secret tls who-tls --cert=tls.crt --key=tls.key
这个时候我们就可以创建一个 HTTPS 访问应用的 IngressRoute 对象了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 cat >> who-ing.yml <<EOF --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingressroute-tls-demo spec: entryPoints: - websecure routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/tls\`) kind: Rule services: - name: whoami port: 80 tls: secretName: who-tls EOF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingressroute-tls-demo2 annotations: kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" spec: tls: - secretName: who-tls rules: - host: whoami.od.com http: paths: - pathType: Prefix path: /ssl backend: service: name: whoami port: number: 80
创建完成后就可以通过 HTTPS 来访问应用了,由于我们是自签名的证书,所以证书是不受信任的:
Traefik
访问:https://whoami.od.com/tls
Ingress
访问https://whoami.od.com/ssl
ingressRouteTCP 简单TCP服务 Traefik2.X 已经支持了 TCP 服务的,下面我们以 mongo 为例来了解下 Traefik 是如何支持 TCP 服务得。
ingreeRouteTCP 官方文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 cat > mongo.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: mongo-traefik labels: app: mongo-traefik spec: selector: matchLabels: app: mongo-traefik template: metadata: labels: app: mongo-traefik spec: containers: - name: mongo image: mongo:5.0 ports: - containerPort: 27017 --- apiVersion: v1 kind: Service metadata: name: mongo-traefik spec: selector: app: mongo-traefik ports: - port: 27017 EOF
创建成功后就可以来为 mongo 服务配置一个路由了。由于 Traefik 中使用 TCP 路由配置需要 SNI
,而 SNI
又是依赖 TLS
的,所以我们需要配置证书才行,如果没有证书的话,我们可以使用通配符 *
进行配置,我们这里创建一个 IngressRouteTCP
类型的 CRD 对象(前面我们就已经安装了对应的 CRD 资源)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cat > mongo-ingressroute-tcp.yaml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRouteTCP metadata: name: mongo-traefik-tcp spec: entryPoints: - mongo routes: - match: HostSNI(\`*\`) services: - name: mongo-traefik port: 27017 EOF
要注意的是这里的 entryPoints
部分,是根据我们启动的 Traefik 的ConfigMap
静态配置中的 entryPoints 来决定的,我们可以自己添加一个用于 mongo 服务的专门入口点。关于 entryPoints 入口点的更多信息,可以查看文档 entrypoints 了解更多信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 vim cm.yml ... entryPoints: web: address: ":80" websecure: address: ":443" traefik: address: ":8090" metrics: address: ":9100" mongo: address: ":27017" ...
然后更新 Traefik 重启即可
1 2 kubectl apply -f cm.yml kubectl delete pods -n kube-system -l app=traefix-v2
创建完成后,同样我们可以去 Traefik 的 Dashboard 页面上查看是否生效:
然后我们配置一个域名 mongo.local
解析到 Traefik 所在的节点,然后通过 27017 端口来连接 mongo 服务:
带 TLS 证书的 TCP 上面我们部署的 mongo 是一个普通的服务,然后用 Traefik 代理的,但是有时候为了安全 mongo 服务本身还会使用 TLS 证书的形式提供服务,将上面证书放置到 certs 目录下面,然后我们新建一个 tls-mongo
的目录,在该目录下面执行如下命令来生成证书:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 生成根证书 # -x509: 用于生成自签证书,如果不是自签证书则不需要此项 # -days: 证书的有效期限,默认是365天 # 直接带参数的输入,直接输密码即可 openssl req -out ca.pem -new -x509 -days 3650 -subj "/C=CN/ST=BeiJing/O=Boysec/CN=server1/CN=Boysec/emailAddress=wangxiansen@boysec.cn" # 密码自行配置即可,passwd # 生成证书私钥 openssl genrsa -out server.key 2048 # 生成证书申请文件 cat server.req # CN=mongo.local 是mongo机器运行的节点域名信息,如果对不上就会报错 openssl req -key server.key -new -out server.req -subj "/C=CN/ST=BeiJing/O=Boysec/CN=server1/CN=Boysec/CN=mongo.local/emailAddress=wangxiansen@boysec.cn" # 生成证书 openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAcreateserial -out server.crt -days 3650 # 合并私钥和公钥,生成server.pem cat server.key server.crt > server.pem
在 tls-mongo/certs
目录下面执行如下命令通过 Secret 来包含证书内容:
1 $ kubectl create secret tls mongo-certs --cert=server.crt --key=server.key
然后重新更新 IngressRouteTCP
对象,增加 TLS 配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: traefik.containo.us/v1alpha1 kind: IngressRouteTCP metadata: name: mongo-traefik-tcp spec: entryPoints: - mongo routes: - match: HostSNI(`mongo.local`) services: - name: mongo-traefik port: 27017 tls: secretName: mongo-certs
同样更新后,现在我们直接去访问应用就会被 hang 住,因为我们没有提供证书,这个时候我们可以带上证书来进行连接
1 2 3 4 5 6 7 8 9 10 $ mongo --host mongo.local --port 27017 --ssl --sslCAFile=./ca.pem --sslPEMKeyFile=./server.pem MongoDB shell version v4.4.24 connecting to: mongodb://mongo.local:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("4a8a66b1-7371-415f-b353-23d71648e054") } MongoDB server version: 5.0.5 WARNING: shell and server versions do not match > show dbs admin 0.000GB config 0.000GB local 0.000GB
可以看到现在就可以连接成功了,这样就完成了一个使用 TLS 证书代理 TCP 服务的功能,这个时候如果我们使用其他的域名去进行连接就会报错了,因为现在我们指定的是特定的 HostSNI:
1 2 3 4 5 6 7 8 9 10 11 mongo --host traefik.od.com --port 27017 --ssl --sslCAFile=./ca.pem --sslPEMKeyFile=./server.pem {"t":{"$date":"2023-07-22T03:52:20.300Z"},"s":"W", "c":"CONTROL", "id":23321, "ctx":"main","msg":"Option: This name is deprecated. Please use the preferred name instead.","attr":{"deprecatedName":"ssl","preferredName":"tls"}} {"t":{"$date":"2023-07-22T03:52:20.300Z"},"s":"W", "c":"CONTROL", "id":23321, "ctx":"main","msg":"Option: This name is deprecated. Please use the preferred name instead.","attr":{"deprecatedName":"sslPEMKeyFile","preferredName":"tlsCertificateKeyFile"}} {"t":{"$date":"2023-07-22T03:52:20.300Z"},"s":"W", "c":"CONTROL", "id":23321, "ctx":"main","msg":"Option: This name is deprecated. Please use the preferred name instead.","attr":{"deprecatedName":"sslCAFile","preferredName":"tlsCAFile"}} MongoDB shell version v4.4.24 connecting to: mongodb://k8s.mongo.local:27017/?compressors=disabled&gssapiServiceName=mongodb Error: couldn't connect to server k8s.mongo.local:27017, connection attempt failed: HostNotFound: Could not find address for k8s.mongo.local:27017: SocketException: Host not found (authoritative) : connect@src/mongo/shell/mongo.js:374:17 @(connect):2:6 exception: connect failed exiting with code 1
ingressRouteUDP 此外 Traefik2.3.x 版本也已经提供了对 UDP 的支持,所以我们可以用于诸如 DNS 解析的服务提供负载。同样首先部署一个如下所示的 UDP 服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 apiVersion: v1 kind: Service metadata: name: whoamiudp spec: ports: - protocol: UDP name: udp port: 8080 selector: app: whoamiudp --- kind: Deployment apiVersion: apps/v1 metadata: name: whoamiudp labels: app: whoamiudp spec: replicas: 2 selector: matchLabels: app: whoamiudp template: metadata: labels: app: whoamiudp spec: containers: - name: whoamiudp image: containous/whoamiudp ports: - name: udp containerPort: 8080
部署完成后我们需要在 Traefik 中定义一个 UDP 的 entryPoint 入口点,修改我们部署 Traefik 的 ConfigMap
文件,增加 UDP 协议的入口点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... entryPoints: web: address: ":80" websecure: address: ":443" traefik: address: ":8090" metrics: address: ":9100" mongo: address: ":27017" udpep: address: ":9300/udp" ...
然后更新 Traefik 重启即可
1 2 kubectl apply -f cm.yml kubectl delete pods -n kube-system -l app=traefix-v2
UDP 的入口点增加成功后,接下来我们可以创建一个 IngressRouteUDP
类型的资源对象,用来代理 UDP 请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cat <<EOF | kubectl apply -f - apiVersion: traefik.containo.us/v1alpha1 kind: IngressRouteUDP metadata: name: whoamiudp spec: entryPoints: - udpep routes: - services: - name: whoamiudp port: 8080 EOF $ kubectl get ingressrouteudp NAME AGE whoamiudp 27s
创建成功后我们首先在集群上通过 Service 来访问上面的 UDP 应用:
1 2 3 4 5 6 7 8 9 10 $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE whoamiudp ClusterIP 192.168.145.194 <none> 8080/UDP 112s $ echo "WHO" | socat - udp4-datagram:192.168.145.194:8080Hostname: whoamiudp-7d968ff858-c425d IP: 127.0.0.1 IP: 172.16.130.8 $ echo "wangxiansen" | socat - udp4-datagram:192.168.145.194:8080 Received: wangxiansen
我们这个应用当我们输入 WHO
的时候,就会打印出访问的 Pod 的 Hostname 这些信息,如果不是则打印接收到字符串。现在我们通过 Traefik 所在节点的 IP(mongo.local)与 9300 端口来访问 UDP 应用进行测试:
我们可以看到测试成功了,证明我就用 Traefik 来代理 UDP 应用成功了。除此之外 Traefik 还有很多功能,特别是强大的中间件和自定义插件的功能(下一章讲),为我们提供了不断扩展其功能的能力,我们完成可以根据自己的需求进行二次开发。