zoukankan      html  css  js  c++  java
  • service与kube-proxy

    Kubernetes Pods 是有生命周期的. 当你使用Deployment来运行你的应用是,它会动态创建Pod,使Pod的数量总是维持着Replicas的数目。每个Pod会获得一个IP,但是在Deployment中,在某一时刻运行的Pod集合可能与稍后运行该应用程序的Pod集合不同。这样就会导致一个问题:在你的集群中如果一些Pods(称之为“后端”)为其他Pods(称之为“前端”)提供功能服务,前端如何发现和跟踪IP地址连接,以便前端来使用后端负载?
    Services 就是为了解决这个问题的,在Kubernetes中,一个Service是一个抽象,它定义了一组逻辑Pods和一个访问这些Pods的策略(有时这种模式被称为微服务)。服务所针对的pod集合通常由选择器决定(请参阅下面的说明,了解为什么需要没有选择器的服务)。
    例如,一个无状态的镜像处理后端,它使用3个副本运行。这些副本是前端使用的后端。虽然组成后端集的实际pod可能会发生变化,但前端客户端不应该知道这一点,也不应该自己跟踪后端集。

    定义service

    Kubernetes 中的service是一个REST对象,类似于Pod.和所有的REST对象一样,您可以将服务定义发布到API服务器以创建新实例。服务对象的名称必须是有效的DNS标签名称。
    例如,假设你有一组Pods,每个Pod都监听TCP 9376端口,并且携带一个标签app=MyApp.这里port是service提供服务的端口,targetPort是后端Pods提供实际服务的端口。

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: MyApp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376
    

    该规范创建了一个名为“my-service”的新服务对象,它的目标是任何带有app=MyApp标签的Pod上的TCP端口9376。Kubernetes为该service分配一个IP地址(有时称为ClusterIP“集群IP”),改地址由服务代理Service proxies使用(请参阅下面的虚拟IP和服务代理)。服务选择器的控制器不断地扫描与它的选择器匹配的pod,然后向端点对象endpoint object(名字也是“my-service”)发布任何更新。

    注意: Service 能够将一个接收 port 映射到任意的 targetPort。 为方便期间,默认情况下,targetPort 将被设置为与 port 字段相同的值。

    不带selector的Services

    Services通常抽象访问Kubernetes Pods,但它们也可以抽象其他类型的后端。例如:

    • 您希望在生产环境中有一个外部数据库集群,但是在测试环境中使用自己的数据库。
    • 您希望将service指向另一个名称空间或另一个集群中的service。
    • 您正在将工作负载迁移到Kubernetes。在评估该方法时,您只在Kubernetes中运行部分后端。
      在这些场景中,您可以定义一个没有Pod选择器的服务。例如:
    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376
    

    由于此服务没有选择器,因此不会自动创建相应的端点对象。您可以手动将服务映射到其运行的网络地址和端口,方法是手动添加一个端点对象:

    apiVersion: v1
    kind: Endpoints
    metadata:
      name: my-service
    subsets:
      - addresses:
          - ip: 192.0.2.42
        ports:
          - port: 9376
    

    Endpoint对象的名称(例如上面的my-service)必须是有效的DNS子域名。

    注意:端点IPs必须不是:loopback(对于IPv4是127.0.0.0/8,对于IPv6是::1/128)或link-local(对于IPv4是169.254.0.0/16和224.0.0.0/24,对于IPv6是fe80::/64)。端点IP地址不能是其他Kubernetes服务的集群IP,因为kube-proxy不支持将虚拟IP作为目标。

    访问没有选择器的服务与访问有选择器的服务一样。在上面的例子中,流量被路由到YAML中定义的单个端点:192.0.2.42:9376 (TCP)。

    Virtual IPs and service proxies

    Kubernetes集群中的每个节点都运行一个kube-proxy。kube-proxy负责为除ExternalName之外的其他类型的服务实现一种形式的虚拟IP (ExternalName服务是一种特殊的服务,它没有选择器,而是使用DNS名称,这里暂不阐述ExternalName)。

    为什么不使用轮询DNS呢?

    一个时常出现的问题是,Kubernetes为什么要依赖代理将入站流量转发到后端。有其他的方法吗?例如,是否可以配置具有多个A值(或IPv6的AAAA)的DNS记录,并依赖于轮询域名解析?
    使用代理服务有以下几个原因:

    • DNS实现不尊重记录ttl的历史由来已久,并且在名称过期后仍缓存它们。
    • 有些应用程序只做一次DNS查找,然后无限期地缓存结果。
    • 即使应用程序和库进行了适当的重新解析,DNS记录上的低ttl或零ttl可能会给DNS带来高负载,从而变得难以管理。

    用户空间代理模式

    在这种模式下,kube-proxy监视Kubernetes endponit 和 service对象的添加和删除。对于每个服务,它在本地节点上打开一个端口(随机选择)。到这个“代理端口”的任何连接都会代理到服务的一个后端pod(通过endpoint报告)。在决定使用哪个后端Pod时,kube-proxy将服务的SessionAffinity设置考虑进去。
    最后,用户空间代理安装iptables规则或者ipvs规则,捕捉到服务的clusterIP(虚拟的)和端口的流量。规则将流量重定向到代理端口,该端口代理后端Pod。
    默认情况下,用户空间模式下的kube-proxy通过循环算法选择后端。

    iptables proxy mode

    这种模式,kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。(proxy启动时会创建informer, 开始监听事件,分别启动协程处理handleAddService、handleUpdateService和handleDeleteService事件。)对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend 组合。

    默认的策略是,kube-proxy 在 iptables 模式下随机选择一个 backend。

    使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理,而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。

    如果 kube-proxy 在 iptable s模式下运行,并且所选的第一个 Pod 没有响应,则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败,并会自动使用其他后端 Pod 重试

    IPVS proxy mode

    在ipvs 模式下,kube-proxy监视Kubernetes服务和端点,调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。 该控制循环可确保 IPVS 状态与所需状态匹配。 访问服务时,IPVS 将流量定向到后端Pods中的一个。

    IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数,但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

    IPVS提供了更多选项来平衡后端Pod的流量。 这些是:

    • rr: round-robin
    • lc: least connection (smallest number of open connections)
    • dh: destination hashing
    • sh: source hashing
    • sed: shortest expected delay
    • nq: never queue

    发布服务 —— 服务类型

    对一些应用(如 Frontend)的某些部分,可能希望通过外部Kubernetes 集群外部IP 地址暴露 Service。

    Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

    Type 的取值以及行为如下:

    • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
    • NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 :,可以从集群的外部访问一个 NodePort 服务。
    • LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
    • ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建。

    注意: 您需要 CoreDNS 1.7 或更高版本才能使用 ExternalName 类型。

    您也可以使用 Ingress 来暴露自己的服务。 Ingress 不是服务类型,但它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。

    查看集群的service: kubectl get service --all-namespaces
    详细查看其中一个service: kubectl edit service -n default kubernetes

    apiVersion: v1
    kind: Service
    metadata:
      creationTimestamp: "2020-01-14T15:23:57Z"
      labels:
        component: apiserver
        provider: kubernetes
      name: kubernetes
      namespace: default
      resourceVersion: "80069036"
      selfLink: /api/v1/namespaces/default/services/kubernetes
      uid: e209f206-36e1-11ea-82d2-6c92bf8d8226
    spec:
      clusterIP: 3.4.5.1
      ports:
      - name: https
        port: 443
        protocol: TCP
        targetPort: 6443
      sessionAffinity: None
      type: ClusterIP
    status:
      loadBalancer: {}
    

    这个service的名字叫kubernetes,是一种clusterIP型的service, 并且没有selector,那么需要创建endpoint对象。我们敲命令kubectl get endpoints --all-namespaces发现一个同名的endpoint对象,使用命令kubectl edit endpoints -n default kubernetes查看这个endpoint的详情。

    查看kube-proxy的配置文件看到--proxy-mode=ipvs --ipvs-scheduler=rr可以知道kube-proxy使用的ipvs proxy mode,然后到每个计算节点上输入命令ipvsadm -Ln,可以看到

    TCP  3.4.5.1:443 rr
      -> 10.243.129.129:6443          Masq    1      0          0         
      -> 10.243.129.130:6443          Masq    1      0          0         
      -> 10.243.129.131:6443          Masq    1      1          0    
    

    service提供的服务是本集群内的3.4.5.1:443访问,通过轮询三个pod的6443端口实现。

    分析Kubernetes v1.14.8版本的kube-proxy代码,总结工作流程如下(ipvs模式):

  • 相关阅读:
    POJ 1840 Eqs 二分+map/hash
    【vijos】P1514天才的记忆
    函数介绍
    函数参数和函数返回值
    函数参数和函数返回值
    前端项目里常见的十种报错及其解决办法
    前端项目里常见的十种报错及其解决办法
    BootstrapTable的使用教程
    BootstrapTable的使用教程
    前端js实现打印(导出)excel表格
  • 原文地址:https://www.cnblogs.com/janeysj/p/12918935.html
Copyright © 2011-2022 走看看