Service
Service 是应用服务的抽象,它定义了一组逻辑 Pod 和访问它们的策略。
1 | yaml复制代码apiVersion: v1 |
上述配置创建了一个名称为 my-service
的服务,其所属的 namespace 为 test。该服务会将请求代理到使用 TCP 端口9376,并且具有标签 app = personal
的Pod上。
服务选择符的控制器不断扫描与其选择符匹配的 Pod,然后发布到 也称为“my-service”的 Endpoint
对象。
需要注意的是,Service 能够将一个接收 port 映射到任意的 targetPort。默认情况下,targetPort 将会设置为与 port 字段相同的值。
1. 虚拟IP 和 Service代理
集群内服务之间的访问,以及 Pod 访问服务,都是由 kube-proxy
负责。
kube-proxy 为 Service 分配了一个 VIP
(Virtual IP),当有流量打到 VIP 时,kube-proxy 会将流量代理到某个 Pod 中。所以,集群内微服务的负载均衡是由 kube-proxy 提供的。
1.1 iptables 代理模式
在该模式下,kube-proxy 对每个 service 都会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的某个 Pod 上。如果第一个Pod没有响应,则连接失败,kube-proxy 会自动选择其他 Pod 重试。
iptables 规则默认的策略是,随机选择一个后端处理请求。
使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理,无需在用户空间和内核空间之间切换。
1.2 IPVS 代理模式(NAT模式)
在 ipvs 模式下,kube-proxy 监视服务和端点,调用 netlink 接口(netlink 是一种用于内核态和用户态进程之间进行数据传输的特殊的 IPC 机制)相应的创建 IPVS 规则,并定期将 IPVS 规则与 service 和 endpoints 同步,确保 IPVS 状态与所需状态匹配。
IPVS 代理模式与 iptables 类似的是,IPVS 也基于 netfilter
,但是使用哈希表作为基础数据结构。
iptables 模式采用一条条的规则列表,又是为了防火墙设计的,集群数量越多 iptable 规则也越多,而且是从上到下匹配。所以,IPVS 模式下(hash 查表)的 kube-proxy 重定向延时要短。
除此之外,ipvs 支持比 iptables 更复杂的负载均衡算,比如轮替、最少链接、从不排队等等。
2. 服务发现
k8s 支持两种基本的服务发现模式——环境变量和DNS。
服务发现完成的工作是什么?
2.1 环境变量
当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。
比如,一个 Service 名称为“redis-master”,暴露了8888端口,同时给它分配的 ClusterIP 为 10.0.0.11,这个 Service 会生成如下环境变量:
1 | yaml复制代码REDIS_MASTER_SERVICE_HOST=10.0.0.11 |
需要注意的是:当我们使用环境变量的方法访问服务的 Pod 时,那我们必须在 Pod 出现之前创建服务,否则不会自动生成 Service 的环境变量。
2.2 DNS
k8s 支持集群的 DNS 服务器(如 CoreDNS)监视集群中的新服务,并为每个服务创建一组 DNS 纪录。
例如,如果你在 Kubernetes 命名空间 my-ns
中有一个名为 my-service
的服务,则 DNS 服务共同为 my-service.my-ns
创建 DNS 记录。 my-ns
命名空间中的 Pod 能够通过按名检索 my-service
来找到服务 (my-service.my-ns
也可以工作)。
其他命名空间中的 Pod 必须将名称限定为 my-service.my-ns
。 这些名称将被解析为对应的 Service IP。
k8s DNS 服务器是唯一一种能够访问 ExternalName 类型的 Service 的方式。
3. 服务类型
对于集群中的某些 Pod,可能希望将其暴露到集群外部访问,如前端。
k8s 允许指定 Service 类型,默认是 ClusterIP。
- ClusterIP:通过集群内部的 IP 暴露服务,但服务只能在集群内部访问。
- NodePort:在节点(虚拟机)上开放一个端口,任何到达该端口的的流量都可以转发到对应服务。 通过请求
<节点 IP>:<节点端口>
,你可以从集群的外部访问一个NodePort
服务。 - LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。外部负载均衡器可以将流量路由到自动创建的
NodePort
和ClusterIP
上。 - ExternalName:通过返回 CNAME 和对应值,可以将服务映射到
externalName
字段的内容。
也可以使用 Ingress
来暴露自己的服务。Ingress 不是一种服务类型,可充当集群的请求入口。Ingress 可以在同一 IP 地址下公开多个服务。
3.1 NodePort
如果将 type
字段设置为 NodePort
,则 K8s 控制平面将在 --service-node-port-range
标志指定的范围内分配端口(默认值:30000-32767)。 每个节点将那个端口(每个节点上的相同端口号)代理到服务中。 除此之外,可以在服务的 .spec.ports[*].nodePort
字段中手动分配端口。
大多数时候我们可以让 k8s 自动分配端口,端口范围为 30000-32767。
1 | yaml复制代码apiVersion: v1 |
3.2 LoadBalancer
设置 type
的值为 "LoadBalancer"
, 将为 Service 提供负载均衡器。负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 Service 的 status.loadBalancer
字段发布出去。
1 | yaml复制代码apiVersion: v1 |
来自集群外部的负载均衡器将流量定向到 service 上,一般用作三层负载均衡。
某些云提供商的负载均衡器允许设置 loadBalancerIP
,将根据用户设置的 loadBalancerIP
来创建负载均衡器。如果没有设置 localBalancerIP
字段,将会给负载均衡器指派一个临时 IP。
3.2 ExternalName
类型为 ExternalName
的服务会将服务映射到 DNS 名称,实现暴露服务的目的。
以下 Service 将名称为my-service
的服务映射到 my.database.example.com
:
1 | yaml复制代码apiVersion: v1 |
当查找服务 my-service.prod.svc.cluster.local
时,集群 DNS 服务返回 CNAME
记录, 其值为 my.database.example.com
。 访问 my-service
的方式与其他服务的方式相同,但主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发。
4. Ingress
Ingress 管理集群中服务的外部访问,典型的访问方式是 HTTP。可用作七层负载均衡,基于域名或路径将流量路由到后端服务。
需要特别注意的是:当使用 Nginx Ingress 作为 Ingress 控制器时,Nginx Ingress 在流量路由时不会使用 service,会使用 Endpoints 绕过 kube-proxy 将流量路由到 Pod,以实现负载均衡。
Ingress 不会公开任意端口或协议。如果想将 HTTP 和 HTTPS 以外的服务公开到外部,通常使用 Service.Type=NodePort
或 Service.Type=LoadBalancer
类型的服务。
如下路由:
基于七层负载均衡生成的 Ingress 对象的 YAML 文件如下:
1 | yaml复制代码apiVersion: extensions/v1beta1 |
本文转载自: 掘金