Kubernetes 核心概念之3 Service

官方文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

前言

我们知道 Deployment 只是保证了支撑服务的微服务 Pod 的数量,但是没有解决如何访问这些服务的问题。

一个 Pod 是一个运行服务的实例,Pod 可能随时在一个节点上停止运行,在另一个节点上以一个新的 IP 启动,因此不能以固定的 IP 和端口号提供服务。

要稳定地提供服务,需要服务发现和负载均衡的能力。

  • 服务发现是对客户端的请求找到对应的后端服务实例。在 Kubernetes 集群中,客户端需要访问的服务就是 Service 对象。每个 Service 会对应一个集群内部有效的 虚拟IP,集群内部通过 虚拟IP 访问一个服务。
  • 在 Kubernetes 集群中,微服务的负载均衡是由 kube-proxy 实现的。kube-proxy 是 Kubernetes 集群内部的负载均衡器。它是一个分布式代理服务器,在 Kubernetes 的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的 kube-proxy 就越多,高可用节点也随之增多。与之相比,我们平时在服务器端使用反向代理作负载均衡,还要进一步解决反向代理的高可用问题。

我猜:通常 一个 web 服务需要创建一个 service。一个端口对应一个应用

kube-proxy 三种代理模式

kubernetes 集群中有三层网络,一类是真实存在的,例如 Node Network、Pod Network,提供真实IP地址;一类是虚拟的,例如 Cluster Network 或 Service Network,提供虚拟 IP 地址,不会出现在接口上,仅会出现在 Service 当中。

kube-proxy 始终 watch(监控)kube-apiserver 上关于 Service 相关的资源变动状态,一旦获取相关信息 kube-proxy 都要把相关信息转化为当前节点之上的,能够实现 Service 资源调度到特定 Pod 之上的规则,进而实现访问 Service 就能够获取 Pod 所提供的服务

kube-proxy 三种代理模式:UserSpace 模式、iptables 模式、ipvs 模式

1 UserSpace 模式

userspace 模式是 kube-proxy 使用的第一代模式,该模式在 kubernetes v1.0 版本开始支持使用。

kube-proxy 会为每个 Service 随机监听一个端口(proxy port),并增加一条 iptables 规则。所以通过 ClusterIP:Port 访问 Service 的报文都 redirect 到 proxy port,kube-proxy 从它监听的 proxy port 收到报文以后,走 round robin(默认) 或是 session affinity(会话亲和力,即同一 client IP 都走同一链路给同一 pod 服务),分发给对应的 pod。

由于 userspace 模式会造成所有报文都走一遍用户态(也就是 Service 请求会先从用户空间进入内核 iptables,然后再回到用户空间,由 kube-proxy 完成后端 Endpoints 的选择和代理工作),需要在内核空间和用户空间转换,流量从用户空间进出内核会带来性能损耗,所以这种模式效率低、性能不高,不推荐使用。

2 iptables 模式

iptables 模式是 kube-proxy 使用的第二代模式,该模式在 kubernetes v1.1 版本开始支持,从 v1.2 版本开始成为 kube-proxy 的默认模式。

iptables 模式的负载均衡模式是通过底层 netfilter/iptables 规则来实现的,通过 Informer 机制 Watch 接口实时跟踪 Service 和 Endpoint 的变更事件,并触发对 iptables 规则的同步更新。

在 iptables 模式下,kube-proxy 只是作为 controller,而不是 server,真正服务的是内核的 netfilter,体现在用户态的是 iptables。所以整体的效率会比 userspace 模式高

3 ipvs 模式

ipvs 模式被 kube-proxy 采纳为第三代模式,模式在 kubernetes v1.8 版本开始引入,在 v1.9 版本中处于 beta 阶段,在 v1.11 版本中正式开始使用。

ipvs(IP Virtual Server) 实现了传输层负载均衡,也就是 4 层交换,作为 Linux 内核的一部分。ipvs 运行在主机上,在真实服务器前充当负载均衡器。ipvs 可以将基于 TCP 和 UDP 的服务请求转发到真实服务器上,并使真实服务器上的服务在单个 IP 地址上显示为虚拟服务。

ipvs 和 iptables 都是基于 netfilter 的,那么 ipvs 模式有哪些更好的性能呢?

  • ipvs 为大型集群提供了更好的可拓展性和性能
  • ipvs 支持比 iptables 更复杂的负载均衡算法(包括:最小负载、最少连接、加权等)
  • ipvs 支持服务器健康检查和连接重试等功能
  • 可以动态修改 ipset 的集合,即使 iptables 的规则正在使用这个集合

ipvs 依赖于 iptables。ipvs 会使用 iptables 进行包过滤、airpin-masquerade tricks(地址伪装)、SNAT 等功能,但是使用的是 iptables 的扩展 ipset,并不是直接调用 iptables 来生成规则链。通过 ipset 来存储需要 DROP 或 masquerade 的流量的源或目标地址,用于确保 iptables 规则的数量是恒定的,这样我们就不需要关心有多少 Service 或是 Pod 了。

使用 ipset 相较于 iptables 有什么优点呢?iptables 是线性的数据结构,而 ipset 引入了带索引的数据结构,当规则很多的时候,ipset 依然可以很高效的查找和匹配。我们可以将 ipset 简单理解为一个 IP(段) 的集合,这个集合的内容可以是 IP 地址、IP 网段、端口等,iptables 可以直接添加规则对这个“可变的集合进行操作”,这样就可以大大减少 iptables 规则的数量,从而减少性能损耗。

举一个例子,如果我们要禁止成千上万个 IP 访问我们的服务器,如果使用 iptables 就需要一条一条的添加规则,这样会在 iptables 中生成大量的规则;如果用 ipset 就只需要将相关的 IP 地址(网段)加入到 ipset 集合中,然后只需要设置少量的 iptables 规则就可以实现这个目标。

Service

Service 是 Kubernetes 的核心概念之一,创建一个 Service 可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。

防止 Pod 失联-服务发现

因为 Pod 每次创建都对应一个 IP 地址,而这个 IP 地址是短暂的,每次随着 Pod 的更新都会变化,假设当我们的前端页面有多个 Pod 时候,同时后端也多个 Pod,这个时候,他们之间的相互访问,就需要通过注册中心,拿到 Pod 的 IP 地址,然后去访问对应的 Pod。

定义 Pod 访问策略-负载均衡

页面前端的 Pod 访问到后端的 Pod,中间会通过 Service 一层,而 Service 在这里还能做负载均衡,负载均衡的策略有很多种实现策略,例如:

  • 随机
  • 轮询
  • 响应比

Pod 和 Service 的关系

Pod 和 Service 之间是根据 label 和 selector 之间的关系建立关联的(和 Controller 一样)。

1
2
3
4
5
6
7
# service 中
selector:
app: redis

# pod 中
labels:
app: redis

Service 常用类型

我们在访问 service 的时候,其实也是需要有一个 ip 地址,这个 ip 肯定不是 Pod 的 ip 地址,而是虚拟IP vip,这个 ip 有以下几种。

Service 常用类型有三种:

  • ClusterIp:集群内部可以访问的 虚拟IP,默认类型。只能通过集群内部访问。
  • NodePort:对外访问应用使用。NodePort 端口范围为:30000-32767
  • LoadBalancer:对外访问应用使用,公有云。使用云提供商的负载均衡器,可以向外部暴露服务,选择该值,外部的负载均衡器可以路由到 NodePort 服务和 Cluster IP 服务;

示例

导出一个包含 service 配置信息的 yaml 文件。

1
kubectl expose deployment web --port=80 --target-port=80 --dry-run -o yaml > service.yaml

service.yaml 如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1  # 版本
kind: Service # 资源对象
metadata: # 元数据
creationTimestamp: null
labels: # 自定义标签属性列表
- app: web
name: web # 指定 service 名称,在同一个namespace中必须唯一
namespace: default # 命名空间。不指定时默认为default命名空间
annotations: # 自定义注解属性列表
- name: string
spec: # 详细描述
type: NodePort # ervice的类型,如果没有设置的话,默认使用 ClusterIp,也就是只能在集群内部使用
ports: # 指定端口
- port: 80 # 指定 service 自己的端口
protocol: TCP # service 使用协议
targetPort: 80 # 容器端口,转发后端容器端口
nodePort: 30007 # 当type=NodePort时,指定映射到物理机的端口号。默认情况下,如不指定,Kubernetes 会从某个范围内分配一个端口号(默认:30000-32767)
selector: # 标签选择器,通过标签匹配关联的pod
app: web # 标签
status:
loadBalancer:
ingress:
ip: string # 外部负载均衡器的IP地址
hostname: string # 外部负载均衡器的主机名

实例

1、创建 service 的 yml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1          # 版本
kind: Service # 资源对象
metadata:
name: my-service # 指定service名称
namespace: default # 命名空间
spec:
clusterIP: 10.0.0.123 # 分配IP
ports:
- name: http # 端口名称
port: 80 # 指定 service 自己的端口
protocol: TCP # service使用协议
targetPort: 80 # 容器端口,转发后端容器的端口
selector:
app: nginx

2、创建 service

1
kubectl apply -f service.yaml

3、查看创建的 service

1
2
3
# kubectl get service 
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.0.0.123 <none> 80/TCP 36s

4、动态感知查看分配到的 Node

1
2
3
4
5
6
# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.1.108:6443,192.168.1.109:6443 2d16h
my-service <none> 2m54s
nginx-service 172.17.1.2:80,172.17.1.3:80,172.17.1.6:80 44h
nginx-service2 <none> 17h

注:每个 service 对应一个 ENDPOINTS 控制器,service 则用来关联 pod。

5、查看 ENDPOINTS 详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# kubectl describe service my-service
Name: my-service
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"my-service","namespace":"default"},"spec":{"clusterIP":"10.0.0.12...
Selector: app=nginx
Type: ClusterIP
IP: 10.0.0.123
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>

最后剩下的一种方式就是 LoadBalanced:对外访问应用使用公有云。

node 一般是在内网进行部署,而外网一般是不能访问到的,那么如何访问的呢?

  • 找到一台可以通过外网访问机器,安装 nginx,反向代理
  • 手动把可以访问的节点添加到 nginx 中

如果我们使用 LoadBalancer,就会有负载均衡的控制器,类似于 nginx 的功能,就不需要自己添加到 nginx 上。


Kubernetes 核心概念之3 Service
https://flepeng.github.io/042-云原生-02-kubernetes-31-核心概念-Kubernetes-核心概念之3-Service/
作者
Lepeng
发布于
2023年3月1日
许可协议