云原生(三)k8s 之服务、负载均衡和网络

Service

Service 是应用服务的抽象,它定义了一组逻辑 Pod 和访问它们的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
yaml复制代码apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: test
spec:
# 选择符
selector:
app: personal
ports:
- protocol: TCP
# 集群给 service 开的端口,节点ip:port,集群内可访问该service
port: 80
# pod的端口,对应 Pod 中的 containerPort
targetPort: 9376

上述配置创建了一个名称为 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 重试。

image.png

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 重定向延时要短。

image.png

除此之外,ipvs 支持比 iptables 更复杂的负载均衡算,比如轮替、最少链接、从不排队等等。

2. 服务发现

k8s 支持两种基本的服务发现模式——环境变量和DNS。

服务发现完成的工作是什么?

2.1 环境变量

当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。

比如,一个 Service 名称为“redis-master”,暴露了8888端口,同时给它分配的 ClusterIP 为 10.0.0.11,这个 Service 会生成如下环境变量:

1
2
3
4
5
6
7
yaml复制代码REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=8888
REDIS_MASTER_PORT=tcp://10.0.0.11:8888
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:8888
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=8888
REDIS_MASTER_PORT_6379_TCP_ADDR=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:使用云提供商的负载均衡器向外部暴露服务。外部负载均衡器可以将流量路由到自动创建的 NodePortClusterIP上。
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
yaml复制代码apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app: MyApp
ports:
- port: 80
targetPort: 80
# 可选字段
# 节点ip:端口,可从集群外部访问该service
nodePort: 30007

3.2 LoadBalancer

设置 type 的值为 "LoadBalancer", 将为 Service 提供负载均衡器。负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 Service 的 status.loadBalancer 字段发布出去。

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
yaml复制代码apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127

来自集群外部的负载均衡器将流量定向到 service 上,一般用作三层负载均衡

某些云提供商的负载均衡器允许设置 loadBalancerIP ,将根据用户设置的 loadBalancerIP 来创建负载均衡器。如果没有设置 localBalancerIP 字段,将会给负载均衡器指派一个临时 IP。

3.2 ExternalName

类型为 ExternalName 的服务会将服务映射到 DNS 名称,实现暴露服务的目的。

以下 Service 将名称为my-service的服务映射到 my.database.example.com:

1
2
3
4
5
6
7
8
yaml复制代码apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com

当查找服务 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=NodePortService.Type=LoadBalancer 类型的服务。

如下路由:

image.png

基于七层负载均衡生成的 Ingress 对象的 YAML 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yaml复制代码apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: other
servicePort: 8080
rules:
- host: foo.mydomain.com
http:
paths:
- backend:
serviceName: foo
servicePort: 8080
- host: mydomain.com
http:
paths:
- path: /bar/*
backend:
serviceName: bar
servicePort: 8080

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%