Kubernetes v1.21 [deprecated]
说明:
此功能特性,尤其是 Alpha 阶段的 topologyKeys API,在 Kubernetes v1.21 版本中已被废弃。Kubernetes v1.21 版本中引入的 拓扑感知的提示, 提供类似的功能。
服务拓扑(Service Topology)可以让一个服务基于集群的 Node 拓扑进行流量路由。 例如,一个服务可以指定流量是被优先路由到一个和客户端在同一个 Node 或者在同一可用区域的端点。
默认情况下,发往 ClusterIP 或者 NodePort 服务的流量可能会被路由到服务的任一后端的地址。Kubernetes 1.7 允许将“外部”流量路由到接收到流量的节点上的 Pod。对于 ClusterIP 服务,无法完成同节点优先的路由,你也无法配置集群优选路由到同一可用区中的端点。 通过在 Service 上配置 topologyKeys,你可以基于来源节点和目标节点的标签来定义流量路由策略。
通过对源和目的之间的标签匹配,作为集群操作者的你可以根据节点间彼此“较近”和“较远” 来定义节点集合。你可以基于符合自身需求的任何度量值来定义标签。 例如,在公有云上,你可能更偏向于把流量控制在同一区内,因为区间流量是有费用成本的,而区内流量则没有。 其它常见需求还包括把流量路由到由 DaemonSet 管理的本地 Pod 上,或者把将流量转发到连接在同一机架交换机的节点上,以获得低延时。
service 的就近转发实际就是一种网络的亲和性,倾向于转发到离自己比较近的 endpoint。在此特性之前,已经在调度和存储方面有一些亲和性的设计与实现:
而 k8s 之前在网络方面还没有亲和性能力,拓扑感知服务路由这个新特性恰好可以补齐这个的空缺,此特性使得 service 可以实现就近转发而不是所有 endpoint 等概率转发。
我们知道,service 转发主要是 node 上的 kube-proxy 进程通过 watch apiserver 获取 service 对应的 endpoint,再写入 iptables 或 ipvs 规则来实现的;而对于 headless service,主要是通过 kube-dns 或 coredns 动态解析到不同 endpoint ip 来实现的。实现 service 就近转发的关键点就在于如何将流量转发到跟当前节点在同一拓扑域的 endpoint 上,也就是会进行一次 endpoint 筛选,选出一部分符合当前节点拓扑域的 endpoint 进行转发。
那么如何判断 endpoint 跟当前节点是否在同一拓扑域里呢?只要能获取到 endpoint 的拓扑信息,用它跟当前节点拓扑对比下就可以知道了。那又如何获取 endpoint 的拓扑信息呢?答案是通过 endpoint 所在节点的 label,我们可以使用 node label 来描述拓扑域。
通常在节点初始化的时候,controller-manager 就会为节点打上许多 label,比如 kubernetes.io/hostname表示节点的 hostname 来区分节点;另外,在云厂商提供的 k8s 服务,或者使用 cloud-controller-manager 的自建集群,通常还会给节点打上 failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region 以区分节点所在可用区和所在地域,但自 v1.17 开始将会改名成 topology.kubernetes.io/zone 和 topology.kubernetes.io/region,参见 PR 81431。
如何根据 endpoint 查到它所在节点的这些 label 呢?答案是通过 Endpoint Slice,该特性在v1.16发布了 alpha,在v1.17进入beta,它相当于 Endpoint API 增强版,通过将 endpoint 做数据分片来解决大规模 endpoint 的性能问题,并且可以携带更多的信息,包括 endpoint 所在节点的拓扑信息,拓扑感知服务路由特性会通过 Endpoint Slice 获取这些拓扑信息实现 endpoint 筛选 (过滤出在同一拓扑域的 endpoint),然后再转换为 iptables 或 ipvs 规则写入节点以实现拓扑感知的路由转发。
之前每个节点上转发 service 的 iptables/ipvs 规则基本是一样的,但启用了拓扑感知服务路由特性之后,每个节点上的转发规则就可能不一样了,因为不同节点的拓扑信息不一样,导致过滤出的 endpoint 就不一样,也正是因为这样,service 转发变得不再等概率,灵活的就近转发才得以实现。
如果集群启用了 ServiceTopology 特性门控, 就可以在 Service 规约中设定 topologyKeys 字段,从而控制其流量路由。 此字段是 Node 标签的优先顺序字段,将用于在访问这个 Service 时对端点进行排序。 流量会被定向到第一个标签值和源 Node 标签值相匹配的 Node。 如果这个 Service 没有匹配的后端 Node,那么第二个标签会被使用做匹配,以此类推,直到没有标签。
如果没有匹配到,流量会被拒绝,就如同这个 Service 根本没有后端。 换言之,系统根据可用后端的第一个拓扑键来选择端点。 如果这个字段被配置了而没有后端可以匹配客户端拓扑,那么这个 Service 对那个客户端是没有后端的,链接应该是失败的。 这个字段配置为 "*" 意味着任意拓扑。 这个通配符值如果使用了,那么只有作为配置值列表中的最后一个才有用。
如果 topologyKeys 没有指定或者为空,就没有启用这个拓扑约束。
一个集群中,其 Node 的标签被打为其主机名、区域名和地区名。 那么就可以设置Service的topologyKeys的值,像下面的做法一样定向流量了。
以下是使用服务拓扑功能的常见示例。
仅路由到节点本地端点的一种服务。如果节点上不存在端点,流量则被丢弃:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
首选节点本地端点,如果节点本地端点不存在,则回退到集群范围端点的一种服务:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
- "*"
首选地域端点而不是区域端点的一种服务。 如果以上两种范围内均不存在端点, 流量则被丢弃。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "topology.kubernetes.io/zone"
- "topology.kubernetes.io/region"
优先选择节点本地端点,地域端点,然后是区域端点,最后才是集群范围端点的 一种服务。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
- "topology.kubernetes.io/zone"
- "topology.kubernetes.io/region"
- "*"
我们创建两个deploment,一个带topologykey的Service,然后来观察创建的ipvs规则是否是按照我们约定的拓扑结构来创建的。
beijing_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ud-test
apps.openyurt.io/pool-name: beijing
name: ud-test-beijing-btwbn
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
app: ud-test
apps.openyurt.io/pool-name: beijing
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: ud-test
apps.openyurt.io/pool-name: beijing
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: apps.openyurt.io/nodepool
operator: In
values:
- beijing
containers:
- image: registry.cn-hangzhou.aliyuncs.com/dice-third-party/nginx:1.14.0
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
hangzhou_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ud-test
apps.openyurt.io/pool-name: hangzhou
name: ud-test-hangzhou-btwbn
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
app: ud-test
apps.openyurt.io/pool-name: hangzhou
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: ud-test
apps.openyurt.io/pool-name: hangzhou
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: apps.openyurt.io/nodepool
operator: In
values:
- hangzhou
containers:
- image: registry.cn-hangzhou.aliyuncs.com/dice-third-party/nginx:1.14.0
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
增加了topoloKeys的字段,用于给kube-proxy做拓扑感知使用,kube-proxy会根据自身的该标签来过滤endpointslice中的endpoin信息设置自己的负载均衡策略,所以相应的节点上也需要打上http://topology.kubernetes.io/zone的标签。
apiVersion: v1
kind: Service
metadata:
name: ud-test
labels:
app: ud-test
spec:
ports:
- name: ud-test
port: 80
targetPort: 80
selector:
app: ud-test
topologyKeys:
- "topology.kubernetes.io/zone"
# - "*"
创建完成后,我们们可以看到一个service资源,对应两个pod资源,同时会生成一个EndpointSlice资源。
然后我们登陆到有http://topology.kubernetes.io/zone节点上的去查看对应的ipvs规则。
可以看到ipvs规则对应的后端pod的ip只有对应节点的标签过滤出来的endpoint信息。
如果节点没有打http://topology.kubernetes.io/zone呢,那就不会有任何后端信息。这也不合理,所有k8s给了一个*选项,如果没有匹配到标签,则后端是所有的endpoint集合。
开启服务拓扑 | Kubernetes
使用拓扑键实现拓扑感知的流量路由 | Kubernetes
我参与的k8s v1.17 新特性: 拓扑感知服务路由
详解K8s资源拓扑感知调度、资源优化策略最佳实践 - 腾讯云开发者社区-腾讯云
如何获取k8s拓扑_k8s从安装到精通--Service 拓扑介绍_weixin_39525243的博客-CSDN博客
Kubernetes 资源拓扑感知调度优化
Kubernetes Service 开启拓扑感知(就近访问)能力_shida_csdn的博客-CSDN博客_k8s 拓扑感知
《重识云原生系列》专题索引:
第四章云网络4.9.1节——网络卸载加速技术综述
第四章云网络4.9.2节——传统网络卸载技术
第四章云网络4.9.3.1节——DPDK技术综述
第四章云网络4.9.3.2节——DPDK原理详解
第四章云网络4.9.4.1节——智能网卡SmartNIC方案综述
第四章云网络4.9.4.2节——智能网卡实现
第六章容器6.1.1节——容器综述
第六章容器6.1.2节——容器安装部署
第六章容器6.1.3节——Docker常用命令
第六章容器6.1.4节——Docker核心技术LXC
第六章容器6.1.5节——Docker核心技术Namespace
第六章容器6.1.6节—— Docker核心技术Chroot
第六章容器6.1.7.1节——Docker核心技术cgroups综述
第六章容器6.1.7.2节——cgroups原理剖析
第六章容器6.1.7.3节——cgroups数据结构剖析
第六章容器6.1.7.4节——cgroups使用
第六章容器6.1.8节——Docker核心技术UnionFS
第六章容器6.1.9节——Docker镜像技术剖析
第六章容器6.1.10节——DockerFile解析
第六章容器6.1.11节——docker-compose容器编排
第六章容器6.1.12节——Docker网络模型设计
第六章容器6.2.1节——Kubernetes概述
第六章容器6.2.2节——K8S架构剖析
第六章容器6.3.1节——K8S核心组件总述
第六章容器6.3.2节——API Server组件
第六章容器6.3.3节——Kube-Scheduler使用篇
第六章容器6.3.4节——etcd组件
第六章容器6.3.5节——Controller Manager概述
第六章容器6.3.6节——kubelet组件
第六章容器6.3.7节——命令行工具kubectl
第六章容器6.3.8节——kube-proxy
第六章容器6.4.1节——K8S资源对象总览
第六章容器6.4.2.1节——pod详解
第六章容器6.4.2.2节——Pod使用(上)
第六章容器6.4.2.3节——Pod使用(下)
第六章容器6.4.3节——ReplicationController
第六章容器6.4.4节——ReplicaSet组件
第六章容器基础6.4.5.1节——Deployment概述
第六章容器基础6.4.5.2节——Deployment配置详细说明
第六章容器基础6.4.5.3节——Deployment实现原理解析
第六章容器基础6.4.6节——Daemonset
第六章容器基础6.4.7节——Job
第六章容器基础6.4.8节——CronJob
第六章容器基础6.4.9.1节——Service综述
第六章容器基础6.4.9.2节——使用 Service 连接到应用
第六章容器基础6.4.9.3节——Service拓扑感知
第六章容器基础6.4.10.1节——StatefulSet概述
第六章容器基础6.4.10.2节——StatefulSet常规操作实操
第六章容器基础6.4.10.3节——StatefulSet实操案例-部署WordPress 和 MySQL
第六章容器基础6.4.10.4节——StatefulSet实操案例-使用 StatefulSet 部署Cassandra
第六章容器基础6.4.10.5节——Statefulset原理剖析
第六章容器基础6.4.11.1节——Ingress综述