Kubernetes (K8S)中Traefik的服务发布

2023-10-17 15:26:14 浏览数 (3)

Kubernetes (K8S)中Traefik的服务发布

王先森2023-08-252023-08-25

简介

traefik 的路由规则就可以实现 4 层和 7 层的基本负载均衡操作,使用 IngressRoute IngressRouteTCP IngressRouteUDP 资源即可。但是如果想要实现 加权轮询、流量复制 等高级操作,traefik抽象出了一个 TraefikService 资源。此时整体流量走向为:外部流量先通过 entryPoints 端口进入 traefik,然后由 IngressRoute/IngressRouteTCP/IngressRouteUDP 匹配后进入 TraefikService,在 TraefikService 这一层实现加权轮循和流量复制,最后将请求转发至kubernetes的service。

除此之外traefik还支持7层的粘性会话、健康检查、传递请求头、响应转发、故障转移等操作。

灰度发布

官方文档

Traefik2.0 的一个更强大的功能就是灰度发布,灰度发布也称为金丝雀发布(Canary),主要就是让一部分测试的服务也参与到线上去,经过测试观察看是否符号上线要求。

比如现在我们有两个名为 appv1appv2 的服务,我们希望通过 Traefik 来控制我们的流量,将 3⁄4 的流量路由到 appv1,1/4 的流量路由到 appv2 去,这个时候就可以利用 Traefik2.0 中提供的带权重的轮询(WRR)来实现该功能,首先在 Kubernetes 集群中部署上面的两个服务。为了对比结果我们这里提供的两个Nginx服务不同页面,方便测试。

appv1 服务的资源清单appv2 服务的资源清单

代码语言:javascript复制
cat > appv1.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: appv1
spec:
  selector:
    matchLabels:
      app: appv1
  template:
    metadata:
      labels:
        use: test
        app: appv1
    spec:
      containers:
      - image: nginx:alpine
        name: appv1
        command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)APP-v1服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
        ports:
            - containerPort: 80
              name: portv1
---
apiVersion: v1
kind: Service
metadata:
  name: appv1
spec:
  selector:
    app: appv1
  ports:
    - name: http
      port: 80
      targetPort: portv1
EOF
代码语言:javascript复制
cat > appv2.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: appv2
spec:
  selector:
    matchLabels:
      app: appv2
  template:
    metadata:
      labels:
        use: test
        app: appv2
    spec:
      containers:
        - name: appv2
          image: nginx:alpine
          command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)APP-v2服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
          ports:
            - containerPort: 80
              name: portv2
---
apiVersion: v1
kind: Service
metadata:
  name: appv2
spec:
  selector:
    app: appv2
  ports:
    - name: http
      port: 80
      targetPort: portv2
EOF

直接创建上面两个服务:

代码语言:javascript复制
$ kubectl apply -f appv1.yml
$ kubectl apply -f appv2.yml
# 通过下面的命令可以查看服务是否运行成功
$ kubectl get pods -l use=test

在 Traefik2.1 中新增了一个 TraefikService 的 CRD 资源,我们可以直接利用这个对象来配置 WRR,之前的版本需要通过 File Provider,比较麻烦,新建一个描述 WRR 的资源清单

代码语言:javascript复制
cat > wrr.yml <<EOF
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
  name: app-wrr
spec:
  weighted:
    services:
      - name: appv1
        weight: 3 # 定义权重
        port: 80
        kind: Service # 可选,默认就是 Service
      - name: appv2
        weight: 1
        port: 80
EOF

然后为我们的灰度发布的服务创建一个 IngressRoute 资源对象

代码语言:javascript复制
cat > wrr-ingressroute.yml <<EOF
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: wrr-ingressroute
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`wrr.od.com`)
      kind: Rule
      services:
        - name: app-wrr
          kind: TraefikService
EOF

在浏览器中连续访问 4 次,我们可以观察到 appv1 这应用会收到 3 次请求,而 appv2 这个应用只收到 1 次请求,符合上面我们的 3:1 的权重配置。

代码语言:javascript复制
[root@ceph01 ~]#curl wrr.od.com
你好, 这是(王先森)APP-v2服务中心
[root@ceph01 ~]#curl wrr.od.com
你好, 这是(王先森)APP-v1服务中心
[root@ceph01 ~]#curl wrr.od.com
你好, 这是(王先森)APP-v1服务中心
[root@ceph01 ~]#curl wrr.od.com
你好, 这是(王先森)APP-v1服务中心

会话保持

官方文档

会话保持功能依赖加权轮询功能

当我们使用 traefik 的负载均衡时,默认情况下轮循多个 k8s 的 service 服务,如果用户对同一内容的多次请求,可能被转发到了不同的后端服务器。假设用户发出请求被分配至服务器 A,保存了一些信息在 session 中,该用户再次发送请求被分配到服务器 B,要用之前保存的信息,若服务器 A 和 B 之间没有 session 粘滞,那么服务器 B 就拿不到之前的信息,这样会导致一些问题。traefik 同样也支持粘性会话,可以让用户在一次会话周期内的所有请求始终转发到一台特定的后端服务器上。

创建 traefikervie 和 ingressRoute,实现基于 cookie 的会话保持

代码语言:javascript复制
cat > cookie.yml <<EOF
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingressroute-sticky
  namespace: default
spec:
  entryPoints:
    - web
  routes:
  - match: Host(`wrr.od.com`)
    kind: Rule
    services:
    - name: sticky
      namespace: default
      kind: TraefikService
---
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
  name: sticky
  namespace: default
spec:
  weighted:
    services:
      - name: appv1
        port: 80
        weight: 1             # 定义权重
      - name: appv2
        port: 80
        weight: 2
    sticky:                   # 开启粘性会话
      cookie:                 # 基于cookie区分客户端      
        name: boysec-cookie   # 指定客户端请求时,包含的cookie名称
EOF

客户端访问测试,携带 cookie

代码语言:javascript复制
[root@ceph01 ~]# for i in {1..5}; do curl -b "boysec-cookie=default-appv2-80" http://wrr.od.com; done
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v2服务中心
[root@ceph01 ~]# for i in {1..5}; do curl -b "boysec-cookie=default-appv1-80" http://wrr.od.com; done 
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心
[root@ceph01 ~]#for i in {1..5}; do curl  http://wrr.od.com; done                           
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v2服务中心
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心
你好, 这是(王先森)APP-v1服务中心

流量复制

官方文档

所谓的流量复制,是一种可以将流入流量复制并同时将其发送给其他服务的方法,镜像服务可以获得给定百分比的请求同时也会忽略这部分请求的响应。,这个功能在做一些压测或者问题复现的时候很有用。

现在我们部署两个 nginx的服务

代码语言:javascript复制
cat > mirror-nginx.yml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: v1
spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
  selector:
    app: v1
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: v1
  labels:
    app: v1
spec:
  selector:
    matchLabels:
      app: v1
  template:
    metadata:
      labels:
        app: v1
    spec:
      containers:
        - name: v1
          image: nginx:alpine
          ports:
            - name: web
              containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: v2
spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
  selector:
    app: v2
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: v2
  labels:
    app: v2
spec:
  selector:
    matchLabels:
      app: v2
  template:
    metadata:
      labels:
        app: v2
    spec:
      containers:
        - name: v2
          image: nginx:alpine
          ports:
            - name: web
              containerPort: 80
EOF

创建一个 IngressRoute 对象,将服务 v1 的流量复制 50% 到服务 v2,如下资源对象所示:

代码语言:javascript复制
cat > mirror-ingress-route.yaml <<EOF
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
  name: app-mirror
spec:
  mirroring:
    name: v1        # 发送 100% 的请求到 K8S 的 Service "v1"
    port: 80
    mirrors:
      - name: v2    # 然后复制 50% 的请求到 v2
        percent: 50
        port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: mirror-ingress-route
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`mirror.od.com`)
      kind: Rule
      services:
        - name: app-mirror
          kind: TraefikService # 使用声明的 TraefikService 服务,而不是 K8S 的 Service
EOF

创建这个资源对象后,在连续访问 4 次 mirror.od.com 可以发现有一半的请求也出现在了 v2 这个服务中。与预期相同,收到了 50% 的流量。

代码语言:javascript复制
# V1 日志
$ kubectl logs -f v1-695b469cd-92jqj 
172.16.130.2 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.130.1"
172.16.120.5 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.120.1"
172.16.100.5 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.100.1"
172.16.130.2 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.130.1"
# V2 日志
$ kubectl logs -f v2-7b889df5d6-q2gkt 
172.16.130.2 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.130.1"
172.16.100.5 - - [25/Aug/2023:07:03:08  0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "172.16.100.1"

0 人点赞