继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

利用拓扑感知路由控制Kubernetes中的流量

料青山看我应如是
关注TA
已关注
手记 328
粉丝 97
获赞 353

你需要了解的简单却强大的Kubernetes流量控制功能简介。

Kubernetes网络是我最喜爱的话题之一,同时也是使用和管理Kubernetes集群中最复杂的部分之一。服务的网络提供了强大的功能,可以轻松部署高可用和分布式的应用,但理解这些高级功能所依赖的网络基础也是非常重要的。特别是在云环境中,流量路径对成本会产生重大影响。

拓扑感知路由 是一个功能,旨在将服务流量保持在同一区域内。例如,一个应用程序的 Pod 可能会通过其 Service 的 Cluster IP 地址连接到一个只读数据库 Pod。拓扑感知路由会尝试将此数据库流量发送到同一区域内的 Pods。由于 Cluster IP 地址由多个端点提供支持,kube-proxy 可以尝试仅将流量发送到同一区域内的端点。

有很多理由让流量保持在区域内,这取决于你的架构。如果你使用云服务,那么一个区域可能代表一个可用区。许多云服务商会对跨越可用区边界的流量收取费用。这样可以减少费用。如果你在本地部署 Kubernetes,你可能会用机架边界来表示区域以进行性能考虑。让流量在同一机架内流动可以为客户端流量提供更优质的服务。

拓扑感知路由 并不总是适用,特别是在流量分布不均衡地跨越区域边界时。官方文档解释了在实现拓扑感知路由时应关注的重要保护措施限制

在这篇文章里,我会带您一步步分析一个使用拓扑感知路由功能的示例工作负载。您将看到如何配置、使用和验证 Kubernetes 网络堆栈中的这个有用的功能。

配置环境

你可以使用运行任何较新版本(≥ 1.27)的 Kubernetes 的任何 Kubernetes 集群。在本文中,我使用 k3d 快速搭建了一个 K3s 集群。你至少需要三个非控制平面节点。这三节点被分成两个区域。

拓扑结构图 — k3d 集群架构

如果你使用 k3d 创建集群,注意截至2023年11月,你需要手动指定 image,以确保拓扑感知路由能正常工作,需要使用更新版本的 K3s 容器。

执行以下命令来创建一个包含6个代理的k3d集群:

    $ k3d cluster create --agents 6 --image=docker.io/rancher/k3s:v1.28.3-rc3-k3s2

一旦集群运行起来,使用 topology.kubernetes.io/zone 标签将工作节点分成两个区。我来自罗切斯特,所以我通常会定义两个区:roc-aroc-b。然而,一个区可以根据您的环境来定义,例如地理位置、建筑或者甚至数据中心的机架。请根据您的具体情况进行适当的命名。如果您使用的是云提供商的 Kubernetes 发行版,这个区域标签可能已经为您自动设置了。

给节点k3d-k3s-default-agent-0打上标签,标记其拓扑区域为roc-a。

然后给k3d-k3s-default-agent-1和k3d-k3s-default-agent-2也打上同样的标签。

接下来,我们把k3d-k3s-default-agent-3, k3d-k3s-default-agent-4和k3d-k3s-default-agent-5的区域标签设为roc-b。

这些命令的目的是将不同节点标记到不同的区域中。

部署一个示例任务

有一个工作负载会很有帮助,来理解拓扑感知路由的效果。这个工作负载应该让你能够轻松观察到流量保持在特定区域内。Nginx 提供了一个简单的方法来做到这一点:你可以在 Nginx 响应头中返回 Pod 主机名。

创建一个配置映射(ConfigMap),包含必要的 Nginx 配置信息。此配置为每个 HTTP 响应设置一个 X-Server-Hostname 响应头,该头包含 Pod 的主机名。虽然你不会在实际生产环境中使用这个配置,但它非常适合这样的练习:它允许你将 HTTP 响应与特定的 Nginx Pod 相关联起来。

---
apiVersion: v1
kind: ConfigMap
元数据:
  名称: nginx.conf
数据:
  默认.conf: |
    服务器 {
        监听       80;
        监听  [::]:80;
        服务器名称  _;

        添加头部 X-Server-主机名 $hostname;

        位置 / {
            根目录   /usr/share/nginx/html;
            索引  index.html index.htm;
        }

        错误页面   500 502 503 504  /50x.html;
        位置 = /50x.html {
            根目录   /usr/share/nginx/html;
        }
    }

接下来,创建一个 Nginx 的 Deployment,使其在拓扑结构中均匀分布到各个区域。为此,指定 6 个副本,以匹配集群中工作节点的数量,并将 topologySpreadConstraint 设置为使用 topology.kubernetes.io/zone 作为 topologyKey。这将使 Pods 尽可能均匀地分布在每个区域。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  # `spec` 部分定义了部署的副本数量和其他一些约束。
  replicas: 6
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      # `topologySpreadConstraints` 用于控制 Pod 在不同拓扑区域的分布。
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          # 当条件无法满足时,不允许调度。
          labelSelector:
            matchLabels:
              app: nginx
      containers:
        - image: docker.io/nginx:latest
          name: nginx
          ports:
            - containerPort: 80
              # `containerPort` 表示容器的端口。
          volumeMounts:
            - name: config
              mountPath: "/etc/nginx/conf.d"
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: nginx.conf
            # `items` 表示配置映射中的项。
            items:
              - key: default.conf
                path: default.conf

注意:在非技术读者阅读时,上述术语可能需要进一步解释。

确认 Pods 正在运行,并且均匀分布在各个区域(zone)。在我的环境中,可以看到在 roc-a 区域的 k3d-k3s-default-agent-1k3d-k3s-default-agent-2 节点上有 3 个 Pods 在运行,其余的 Pods 则在 roc-b 区域的节点上:

使用kubectl获取pod信息
$ kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,STATUS:.status.phase --sort-by .spec.nodeName  
NAME                     节点                      状态  
nginx-5455fc8796-vjq78   k3d-k3s-default-agent-1   运行中  
nginx-5455fc8796-wtrqv   k3d-k3s-default-agent-1   运行中  
nginx-5455fc8796-4qgrp   k3d-k3s-default-agent-2   运行中  
nginx-5455fc8796-sv9cg   k3d-k3s-default-agent-3   运行中  
nginx-5455fc8796-wx4dk   k3d-k3s-default-agent-4   运行中  
nginx-5455fc8796-rtdwt   k3d-k3s-default-agent-5   运行中
创建一个具有拓扑感知的服务

启用拓扑感知路由非常简单:只需将 Serviceservice.kubernetes.io/topology-mode 注解设置为 AutoEndpointSlice 控制器将尝试为 EndpointSlice 中的每个端点分配提示信息,以将流量引导到给定区域内的各个 Pods。正如前面提到的,了解 EndpointSlice 控制器和 kube-proxy 实现的安全措施和限制很重要,尤其是当路由不符合预期时。

创建一个新的 Service 来实现基于拓扑的路由功能。

    ---  
    apiVersion: v1  
    kind: Service  
    metadata:  
      annotations:  
        service.kubernetes.io/topology-mode: Auto  
      name: nginx  
    spec:  
      ports:  
      - port: 80  
        protocol: TCP  
        targetPort: 80  
      selector:  
        app: nginx  
      type: 集群IP

一旦创建了Service,你应该确认每个EndpointSlice中的提示已显示在每个端点地址上。这些提示被kube-proxy使用,并用于决定如何路由流量。在这种情况下,当kube-proxy看到发往NginxService的流量时,它会尽量将流量留在同一区域内的。

首先,确保检查 Nginx 的 ServiceEvents,并确认拓扑感知路由功能已开启:

    $ kubectl events --for service/nginx  
    最后看到   类型     原因                      对象          消息  
    28s         正常     拓扑感知提示已启用       Service/nginx   已启用拓扑感知提示,地址类型为:IPv4  
    $

请检查与 Service 相关的 EndpointSlice,并确认每个端点的 hints.forZones 值是否正确地指定了区域名,确保其指向正确的区域。

    # 列出端点切片列表
    $ kubectl get endpointslice (命令)
    NAME          ADDRESSTYPE   PORTS   ENDPOINTS                                   AGE  
    kubernetes    IPv4          6443    10.89.1.90                                  24m  
    nginx-vq9k2   IPv4          80      10.42.1.3,10.42.4.4,10.42.1.4 + 3 more...   73s  

    # 检查 Nginx 的端点切片,这里只展示一个端点作为示例。
    $ kubectl get endpointslice nginx-vq9k2 -o yaml (输出)
    地址类型为 IPv4
    apiVersion: discovery.k8s.io/v1
    端点如下:
    - 地址:
      - 10.42.1.3
      条件:
        ready: true
        serving: true
        terminating: false
      提示:
        forZones:
        - name: roc-a
      节点名称为:k3d-k3s-default-agent-1
      目标引用详情如下:
        kind: Pod
        name: nginx-5455fc8796-wtrqv
        namespace: default
        唯一标识符为:efd865ed-ebb3-4f13-b39e-2dafeed6fc36

控制平面在拓扑感知路由决策上非常谨慎。如果遇到问题,例如 Service 事件中的错误信息,或者看到 EndpointSlice 中没有填充提示,那么你应该检查你的拓扑标签是否已经正确应用,并且确认 Nodes 是否报告了可分配的 CPU 资源。

创建一个示例客户工作负载

现在一切就绪,拓扑感知路由可以开始工作了。你可以通过一个简单的测试工作负载来确认流量是否按预期流动。一个简单的方法是使用一个不断查询Nginx Service 并只保留 X-Server-Hostname 头信息的 curl 容器。

下面的 Deployment 定义创建了一个工作负载,该工作负载每秒使用 curl 连接到 Nginx,并通过 grep 过滤 X-Server-Hostname 响应头。这个循环每秒执行一次,以模拟对服务的持续工作负载。之后,您可以检查日志以确认流量是否仍然在同一个区域中。

你也希望确保这份工作负载在两个区域间均匀分布。你可以再次使用 topologySpreadConstraints 来确保每个副本放置在不同的区域中。设置副本数量为 3,这样可以确保每个区域中至少有一个 Pod,因为 topologySpreadConstraint 最多容忍一个 Pod 的差异。

---
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: curl  
spec:  
  replicas: 3  
  selector:  
    matchLabels:  
      app: curl  
  template:  
    metadata:  
      creationTimestamp: null  
      labels:  
        app: curl  
    spec:  
      topologySpreadConstraints:  
        # 拓扑扩散约束,用于控制部署在集群中的Pod分布  
        - maxSkew: 1  
          topologyKey: topology.kubernetes.io/zone  
          whenUnsatisfiable: DoNotSchedule  
          # 当不满足拓扑扩散约束时,不允许调度Pod  
          labelSelector:  
            matchLabels:  
              app: curl  
              # 匹配标签,用于选择具有特定标签的应用程序  
      containers:  
        - name: curl  
          image: docker.io/curlimages/curl:latest  
          args:  
            - sh  
            - -c  
            - 'while true; do curl -s -v nginx 2>&1 | grep X-Server-Hostname; sleep 1; done;'
            # 这是一个循环执行curl命令并抓取nginx服务器主机名的shell脚本。
看看结果

你现在有一个Nginx工作负载,以及一个与之对应的使用拓扑感知路由的Service。你还在每个区域部署了一个简单的客户端。你可以查看curl工作负载的日志,确认拓扑感知路由是否按预期工作。

我们这里需要画个拓扑图。

首先列出如下这几个工作负载 DeploymentPods。我有一个 Podroc-a 区域的节点上运行(curl-5fcbf7c896-fxx56),另外有两个 Podroc-b 区域的节点上运行(curl-5fcbf7c896-22qbmcurl-5fcbf7c896-7s5z7

    $ kubectl get pods -l app=curl -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,STATUS:.status.phase --sort-by .spec.nodeName  
    # 运行以下命令以获取名为 'curl' 的 pod 的信息:
    NAME                    NODE                      STATUS  
    curl-5fcbf7c896-fxx56   k3d-k3s-default-agent-0   Running  # 运行状态
    curl-5fcbf7c896-22qbm   k3d-k3s-default-agent-3   Running  # 运行状态
    curl-5fcbf7c896-7s5z7   k3d-k3s-default-agent-4   Running  # 运行状态

    # 注:名称、节点和状态分别是 pod 的名称、运行节点名和当前状态。这些名称和节点名是占位符,在实际环境中可能有所不同。

名称 节点 状态

首先,查看 us-east-1a 可用区中的某个 Pod 的日志:

    $ kubectl logs curl-5fcbf7c896-fxx56 | head  # 使用 kubectl 查看 curl-5fcbf7c896-fxx56 的日志,并显示前几行
    < X-Server-Hostname: nginx-5455fc8796-vjq78  # 服务器主机名是 nginx-5455fc8796-vjq78
    < X-Server-Hostname: nginx-5455fc8796-4qgrp  # 服务器主机名是 nginx-5455fc8796-4qgrp
    < X-Server-Hostname: nginx-5455fc8796-wtrqv  # 服务器主机名是 nginx-5455fc8796-wtrqv

Note: Removed redundant lines to enhance fluency and provided explanations for clarity.

你应该有许多日志行记录,具体数量取决于你的工作负载运行的时间长短。使用一个简单的 bash 一行命令来过滤这些日志,仅显示唯一 Pod 主机名的个数。

$ kubectl logs curl-5fcbf7c896-fxx56 | cut -f 2 -d ':' | sort | uniq -c  # 查看curl-5fcbf7c896-fxx56的日志并处理数据
       2296  nginx-5455fc8796-4qgrp  
       2313  nginx-5455fc8796-vjq78  
       2326  nginx-5455fc8796-wtrqv  # 以下是每个nginx容器的调用次数统计

尽管在Deployment中有6个可用pod,所有的流量都被发送到了3个Nginx pod。确认这些pod都在us-east-1a可用区的Nodes上。

    $ kubectl get pods nginx-5455fc8796-4qgrp nginx-5455fc8796-vjq78 nginx-5455fc8796-wtrqv -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName  (显示指定pod的名称和节点)
    NAME                     NODE  
    nginx-5455fc8796-4qgrp   k3d-k3s-default-agent-2  
    nginx-5455fc8796-vjq78   k3d-k3s-default-agent-1  
    nginx-5455fc8796-wtrqv   k3d-k3s-default-agent-1

看看 us-east-1b 可用区中的 Pod 日志:

    $ kubectl logs curl-5fcbf7c896-22qbm | cut -f 2 -d ':' | sort | uniq -c  
       2324  nginx-5455fc8796-rtdwt  
       2369  nginx-5455fc8796-sv9cg  
       2242  nginx-5455fc8796-wx4dk

请注意,所有流量只发送给3个Nginx Pods(注:Pods是容器编排中的术语)。确认这3个Pods是否都在us-east-1b区域。

    $ kubectl get pods nginx-5455fc8796-rtdwt nginx-5455fc8796-sv9cg nginx-5455fc8796-wx4dk -o custom-columns=名称:.metadata.name,节点:.spec.nodeName  
    名称                     节点  
    nginx-5455fc8796-rtdwt   k3d-k3s-default-agent-5  
    nginx-5455fc8796-sv9cg   k3d-k3s-default-agent-3  
    nginx-5455fc8796-wx4dk   k3d-k3s-default-agent-4:

根据这次实验,拓扑感知路由正在正确运作:客户端到 Nginx Service 的流量只会被发送到与客户端位于同一区域的 Pods。比如在 AWS 中,跨可用区的流量会产生费用,那么你就可以省下这些费用。如果你将你的本地环境按机架划分成不同的区域,那么你可能会从这种配置中获得性能上的好处。

搞定

拓扑感知路由是一种简单却有效的流量管理方式,可以在Kubernetes中进行基本的流量控制。虽然它并不适用于所有工作负载,但在具有均匀分布区域流量的环境中,它会更加有用。了解如何利用像拓扑感知路由这样的功能来管理流量,可以帮助您降低成本并提高应用程序性能。

本文中,你将有机会了解、配置并验证基于示例工作负载的拓扑感知路由。在确定实施策略时,你应该用自己环境中的真实流量模式测试此功能。希望你能找到使用拓扑感知路由的方法来提高应用程序的成本效益、性能和可用性。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP