本文最后更新于 2025-03-05,文章内容可能已经过时。
Kubernetes 中有很多的组件,底层的 Pod 在提供服务能力的同时也需要同各个组件交互,但是 Pod 的声明周期是不停的在变化的,一旦销毁重启之后 IP 就会面临重新分配,这个时候我们之前进行记录的通信地址将不可用,为此就需要我们的 service 来提供服务发现的功能,外部的交互通过 service 来进行流量的转发。
1、定义
service 和 Pod 一样是 Kubernetes 中的一个资源对象,同样可以使用 api 的方式来进行创建。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: test
ports:
- protocol: TCP
port: 80
targetPort: 8080上面的 yaml 文件可以创建一个 service 对象,此服务开放 80 端口并将进来流量转发给具有 label:app=test Pod 的 8080 端口,实现流量到具体的处理 Pod。service 会被指派一个 IP 地址来访问,一般会出现一个同服务名一致的 Endpoints,里面定义了具体的 IP 地址信息,也就是处理结果会到这个对象中。
1.1 type
按照不同的服务提供方式,service 有不同的服务类型来支持,通过 kubectl explain service.spec.type 可以查看到具体的类型选项,一共有四种类型 ExternalName、ClusterIP、NodePort、LoadBalancer,最后一个负载均衡的服务一般都是由厂商自己实现的,Kubernetes 官方并没有进行标准的实现。
ExternalName:该种类型不会自动的创建相关的代理内容,而是会返回
externalName字段中所标识的内容,相当于返回了一个在外部集群中的服务别名。LoadBalancer:使用云提供商的负载均衡器向外部暴露服务,外部负载均衡器可以将流量路由到自动创建的
NodePort服务和ClusterIP服务上,这样就可以从外部访问集群内部的 backend 提供的服务,并且还可以支持多个服务之间的流量负载以及点对点的服务路由等等功能。ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问,默认创建类型。
NodePort:通过 节点IP:port 的方式来暴露服务,会被路由到一个自动创建的 ClusterIP 的服务上,支持集群外部的服务访问。
1.2 selector
我们在定义 service 的信息时候会指定这个字段信息,他的目的就是去寻找当前 service 所关联的 backend Pod 并且创建相关的 Endpoints 来确定 IP 的分配,但是在某些情况中,我们不需要指定该字段,也就是无法自动创建这样一个 Endpoints 来关联具体 Pod,反而抽象出其他类型的 backend。
希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库;
希望服务指向另⼀个 Namespace 中或其它集群中的服务;
正在将工作负载转移到 Kubernetes 集群,和运行在 Kubernetes 集群之外的 backend。
在上面这种场景中我们就需要自己定义这样的一个 Endpoints 来进行映射,需要注意的一点Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 linklocal(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
1.3 VIP
这个 VIP 和我们日常生活中各类的 VIP 有所不同,它指的是 virtual IP 也就是虚拟 IP 的意思,每个 Node 运行⼀个 kube-proxy 进程为 Service 实现虚拟 IP。
userspace:kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。对每个 Service ,它会在本地 Node 上打开⼀个端口(随机选择)。任何连接到服务的请求,都会被代理到 Service 的 backend Pods 中的某个上面,具体哪个 backend Pod ,是基于 Service 的 SessionAffinity 策略来确定的(默认是 round-robin 轮询的方式)。
iptables:对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的⼀组 backend 中的某个(默认是随机选择的方式)。
ipvs:kube-proxy 会监视 Kubernetes Service 对象和 Endpoints,调用 netlink 接口以相应地创建 ipvs 规则并定期与Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望⼀致。访问服务时,流量将被重定向到其中⼀个后端 Pod。
2、拓扑感知路由
拓扑感知路由指的是客户端对⼀个服务的访问流量,可以根据这个服务的端点拓扑,优先路由到与该客户端在同⼀个节点或者可用区的端点上的路由行为(也就是让流量的分发具有的优先级设置)。
2.1 EndpointSlice
端点切片提供了⼀种简单的方法来跟踪 Kubernetes 集群中的网络端点。它们为 Endpoint 提供了⼀种可伸缩和可拓展的替代方案,同时还可以被用到拓扑感知路由中。
这里不介绍端点切片的具体使用,在他的定义中会有几个字段来提供服务的拓扑信息:
nodeName- 端点所在的 Node 名称;zone- 端点所处的可用区;hostname- 端点的 pod 名称。
2.2 启用拓扑感知
启用 kube-apiserver、kube-controller-manager 和 kube-proxy 的特性门控 TopologyAwareHints。通过把 Service 中的注解 service.kubernetes.io/topology-awarehints 的值设置为 auto 就可以激活服务的拓扑感知提示功能。 端点切片的控制器在它认为安全的时候来设置拓扑提示,代理会根据具体的提示过滤由它负责路由的端点。
开启之后端点切片中就会出现 hints 这样的一个字段,表示拥有对应信息的客户端会优先访问到当前的端点中。
2.3 端点切片管理
端点切片和 Endpoint 一样都是给 service 进行服务的,负责给该服务跟踪记录其端点信息,也就是一个从属关系,可以通过为 EndpointSlice 设置一个 own 引用,同时设置 kubernetes.io/service-name 标签来申明具体所属服务名字,这样的话方便控制器对隶属于某个服务的所有 EndpointSlice 进行查找。
EndpointSlice 是由 controller 进行管理的,特别是在服务网格中,会出现其他额外的控制器来对额外的端点切片进行管理,为了避免这些控制器之间的管理冲突,由 endpointslice.kubernetes.io/managed-by 标签来指定具体的控制器,这样就能区分不同的管理。
3、Ingress
service 作为网络服务的主力军,其主要的方向还是为集群内部服务的,器群外部通过 nodeIP 访问的形式也比较少,而 Ingress 是从 Kubernetes 集群外部访问集群内部服务的⼊口,两者相配合来完成服务的流量路由(Ingress 更像是介于集群内部 service 和外部的 internet 的一个桥接)。
Ingress controller 负责实现 Ingress,通常使用负载均衡器,它还可以配置边界路由和其他前端,这有助于以高可用的方式处理流量。
3.1 资源定义
需要 Ingress Controller 来实现 Ingress ,单纯的创建⼀个 Ingress 没有任何意义,流量不会自动转发。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: enter
spec:
rules:
- host: "beordie.com"
http:
paths:
- path: /path
backend:
serviceName: service
servicePort: 80[[Ingress]] 需要定义流量的入栈匹配规则,目前支持的是 http:
每条 http 规则包含以下信息:⼀个 host 配置项(如:beordie.com),path 列表(如:/path),每个 path 都关联⼀个 backend,所有的入站请求都要先匹配 host 和 path;
backend 是⼀个 service:port 的组合,的流量被转发到它所匹配的 backend 中。
3.2 类型
单 service:指定⼀个没有 rule 的默认 backend 的方式。
spec:
backend:
serviceName: service
servicePort: 80创建资源之后我们可以查看到一个 IP 地址,这个就是 Ingress controller 生成的 IP,通过这个 IP 的流量都会被转发到 backend 当中去。
多 service:通过在 paths 下面指定规则就可以了,查看 ingress 的具体信息时会在最后得到具体的访问地址。
基于名称:这个就是指定多个的 host,这样用域名来进行划分,不过都是一个入口 IP。
默认:一个没有配置规则的流量会被转发到一个默认的 backend 中,可以用来作为 404 的入口。
3.3 TLS
[[Ingress]] 还支持 tls 的加密,但是仅支持单个 443 端口加密,如果需要使用的话需要定义一个 secret 中指定 tls.crttls.key 这两个信息,之后在文件中进行指定即可 spec.tls.secretName: secretName 。
各种 Ingress controller ⽀持的 TLS 功能之间存在差距,使用的时候需要参阅特定 Ingress controller 的文档,以了解 TLS 在环境中的工作原理。