Kubernetes Service 使用介绍

71

Kubernetes(K8S)中的 Service 是一种抽象层,用于为一组动态变化的 Pod 提供稳定的网络访问入口,解决 Pod 因重启、扩展或故障导致 IP 变化的问题。Service的作用是提供稳定的网络访问,服务发现,负载均衡,支持不同的暴露方式,适用于集群内部或外部的访问需求。

Service 的核心作用

服务发现稳定访问:为动态变化的 Pod 分配固定虚拟 IP(Cluster IP)或 DNS 名称,屏蔽后端 Pod 的变动。

负载均衡:将请求自动分发到多个 Pod,确保流量均衡。

Service 工作原理

标签选择器(Selector):通过 selector 匹配一组 Pod(如 app: web),动态维护后端端点列表。

kube-proxy:组件 kube-proxy 负责维护网络规则,支持两种模式:

  • iptables:通过 Linux 内核的 iptables 规则转发流量。

  • IPVS:基于内核级负载均衡,性能更优,适合大规模集群。

Endpoint 对象:自动创建 Endpoints 对象,记录当前匹配 Pod 的 IP 和端口。

  • 用于指定与一个 Service 关联的后端 Pod 的 IP 地址和端口信息。

  • 充当服务发现机制的一部分,它告诉 Kubernetes 如何将流量路由到 Service 的后端 Pod。

  • Endpoints 一般都是通过 Service 自动生成的,Service 会自动跟踪关联的 Pod,当 Pod 状态发生变化时会自动更新 Endpoints。

Service 访问及负载均衡原理

每个节点都运行着一个 kube-proxy 组件,这个组件会跟踪 Service 和 Pod 的动态变化,并且最新 的 Service 和 Pod 的映射关系会被记录到每个节点的 iptables 中,这样每个节点上的 iptables 规则都会随着 Service 和 Pod 资源自动更新。

iptables 使用 NAT 技术将虚拟 IP(也叫做 VIP)的流量转发到 Endpoint

Service 的类型

Kubernetes 提供了多种类型的 Service,包括 ClusterIP、NodePort、LoadBalancer、Headless 和 ExternalName,每种类型服务不同的需求和用例。 Service 类型的选择取决于你的应用程序的具体要求以及你希望如何将其暴露到网络中。

ClusterIP

原理:使用这种方式发布时,会为 Service 提供一个固定的集群内部虚拟 IP,供集群内(包含节点)访问。

场景:内部数据库服务、内部 API 服务等

NodePort

原理:通过每个节点上的 IP 和静态端口发布服务。 这是一种基于 ClusterIP 的发布方式,因为它应用后首先会生成一个集群内部 IP, 然后再将其绑定到节点的 IP 和端口,这样就可以在集群外通过 nodeIp:port 的方式访问服务。

场景:Web 应用程序、REST API 等

LoadBalancer

原理:这种方式又基于 NodePort,另外还会使用到外部由云厂商提供的负载均衡器。由后者向外发布 Service。 一般在使用云平台提供的 Kubernetes 集群时,会用到这种方式。

场景:Web 应用程序、公开的 API 服务等。

Headless

原理:这种方式不会分配任何集群 IP,也不会通过 Kube-proxy 进行反向代理和负载均衡,而是通过 DNS 提供稳定的网络 ID 来访问, 并且 DNS 会将无头 Service 的后端解析为 Pod 的后端 IP 列表,以供集群内访问(不含节点),属于向内发布。

场景:一般提供给 StatefulSet 使用。

ExternalName

原理:与上面提到的发布方式不太相同,这种方式是通过 CNAME 机制将外部服务引入集群内部,为集群内提供服务,属于**向内发布 **。

场景:连接到外部数据库服务、外部认证服务等。

Service 类型之 ClusterIP

ClusterIP 通过分配集群内部 IP 来在集群内(包含节点)暴露服务,这样就可以在集群内通过 clusterIP:port 访问到 pod 服务,集群外则无法访问。 ClusterIP 又可以叫做 Service VIP(虚拟 IP)。

这种方式适用于那些不需要对外暴露的集群内基础设施服务,如节点守护 agent/数据库等

例:编写 service 配置文件 service-clusterip.yaml

# service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: service-hellok8s-clusterip
spec:
  type: ClusterIP  # 这行是默认的,可省略
#  sessionAffinity: ClientIP # or None, 设置会话亲和性(ClientIP表示同一客户端ip的请求会路由到同个Pod)
#  sessionAffinityConfig:
#    clientIP:
#      timeoutSeconds: 3600 # 范围 0~86400,默认10800(3h)
  selector:
    app: hellok8s  # 通过selector关联pod组
  ports:
    - port: 3000 # service端口
      targetPort: 3000 # 后端pod端口

ClusterIP 除了在节点上可直接访问,在集群内也是可以访问的。

Service 类型之 NodePort

ClusterIP 只能在集群内访问 Pod 服务,而 NodePort 则进一步将服务暴露到集群节点的静态端口上,可以认为 NodePort 是 ClusterIP 的增强模式。

比如 k8s 集群有 2 个节点:node1 和 node2,暴露后就可以通过 node1-ip:port 或 node2-ip:port 的方式来稳定访问 Pod 服务。

例:编写 service 配置文件 service-nodeport.yaml

# service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: service-hellok8s-nodeport
spec:
  type: NodePort
  selector:
    app: hellok8s
  ports:
    - port: 3000  # pod端口
      nodePort: 30000  # 节点固定端口。在NodePort类型中,k8s要求在 30000-32767 范围内,否则apply报错
    # 若需要暴露多个端口,则按下面形式
#    - name: http
#      protocol: TCP
#      port: 80
#      targetPort: 9376
#    - name: https
#      protocol: TCP
#      port: 443
#      targetPort: 9377

Service 类型之 LoadBalancer

NodePort 是通过节点端口的方式向外暴露服务,这其实已经距离集群外访问服务只有一步之遥了。此时我们有两种方式在集群外访问服务:

  • 第一种是比较简单的方式:使用节点的公网 IP 进行访问(将节点 IP 配置到域名 A 记录同理)

  • 第二种是比较稳妥的方式:单独部署一套负载均衡服务(它会再提供一个 VIP 供外部访问),负责将集群外部的流量转发到集群内部。

负载均衡服务一般是做四层转发(大部分也支持七层转发),主要作用是防 DDos 攻击以及提高应用并发的能力。

现代云厂商一般都有提供软件负载均衡服务产品,并且支持按需的防 DDos 攻击能力、跨地区容灾等 Nginx 具备或不具备的能力。

LoadBalancer 正是通过使用云厂商提供的负载均衡器(Service LoadBalancer,一般叫做 SLB)的高可用方式向外暴露服务。 负载均衡器将集群外的流量转发到集群内的 Node,后者再基于 ClusterIP 的方式转发到 Pod。可以说 LoadBalancer 是 NodePort 的进一步增强。

假如你在 AWS 的 EKS 集群上创建一个 Type 为 LoadBalancer 的 Service。它会自动创建一个 ELB (Elastic Load Balancer) ,并可以根据配置的 IP 池中自动分配一个独立的 IP 地址,可以供外部访问。

从架构图可看出,LoadBalancer是基于 NodePort 的一种 Service 。

例:编写 service 配置文件 service-loadbalancer.yaml

# service-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
  name: service-hellok8s-loadbalancer
  annotations: # 这里是使用阿里云私网SLB的配置示例,SLB一般使用注解来控制LB的具体行为
    # 指明SLB实例地址类型为私网类型。
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: intranet
    # 修改为您的私网SLB实例ID。
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: <YOUR_INTRANET_SLB_ID>
    # 是否自动创建SLB端口监听(会覆写已有端口监听),也可手动创建端口监听。
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-force-override-listeners: 'true'
spec:
  type: LoadBalancer
  selector:
    app: hellok8s
  ports:
    - port: 80
      name: http
      targetPort: 80
    - port: 443
      name: https
      targetPort: 443
#  externalTrafficPolicy: Local # or Cluster, Local表示保留客户端源IP,Cluster(默认)表示不保留源IP但有较好的集群内负载均衡效果
#  healthCheckNodePort: 80 # 可选,设置节点的健康检查端口(不设置也会自动选择一个端口用作健康检查)

LoadBalancer 类型的 Service 本质上是由云厂商提供具体实现,大部分云厂商都支持四层和七层协议代理。

https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/configure-an-ingress-controller-to-use-an-internal-facing-slb-instance?spm=a2c4g.11186623.0.0.5d1736e0l59zqg

服务发现

k8s 支持下面两种服务发现方式:kube-dns(推荐)、环境变量

kube-dns

kube-system 空间下有个名为 kube-dns 的 service,这个 service 就是 k8s 内置的 DNS 组件, 它用来为集群中所有 Pod 提供服务发现功能。

这个名为 kube-dns 的 service 通过 selectork8s-app=kube-dns 关联了名为 coredns 的 Pod 组

k8s 通过每个节点部署的 kubelet 组件向每个新启动的 Pod 注入 DNS 配置(通过/etc/resolv.conf),从而实现服务发现。

环境变量

在每个新启动的 Pod 中,kubelet 也会向其以环境变量形式注入当前 Namespace中已存在的 Service 连接信息,Pod 可以通过这些环境变量来发现其他 Service 的 IP 地址。

关闭 Service Env 注入

在一个 Namespace 中若存在 1000 个 Service,就会给新创建的 Pod 注入 7000 个 Env 变量。 巨多的 Env 变量会导致某些编程语言开发的应用所运行的容器崩溃(也可能表现为容器的 CPU/内存占用高),比如 Java,Nodejs 等。 所以我们可以在创建 Pod 时,通过设置spec.enableServiceLinks: false参数来关闭 Service Env 注入。 关闭 Service Env 注入不会影响模板中的其他 Env 变量注入