kubenetes 四种 Service 类型

1.K8S Service 介绍

Service 是什么?

Service 是 Kubernetes 集群中的一个资源对象,用于定义如何访问一组带有相同特征的 Pods,提供统一的访问入口 + 负载均衡。通常情况下,Service 通过 Label Selector 来确定目标 Pods,ExternalName Services 例外,关于 Service 的详细介绍,请参阅官方文档中 Service 章节

Kubernetes 提供了四种类型的 Service,分别用于不同的业务场景,默认的类型是 ClusterIP 。

  • ClusterIP
  • NodePort
  • LoadBalance
  • ExternalName

iptables 或者 ipvs 是 Service 的具体实现手段,为一组 Pods 提供负载均衡能力。

Pods 与 Service 的关系是:Service 通过 Pods 的 label 标签关联一组 Pods

Service 工作流程:

流程:客户端 -> NodePort/ClusterIP (iptables/ipvs负载均衡规则) -> 各节点的 Pod

1
2
3
4
5
6
7
# 查看
$ kubectl get service
$ kubectl get svc

# 查看 services 与 pod 的关联信息:services 关联哪些 pod的真实ip
$ kubectl get endpoints
$ kubectl get ep

2.K8S 四种 Service 资源类型

(1)ClusterIP 集群内部访问

ClusterIP 是 Kubernetes 中默认的服务类型。分配一个稳定的IP,即VIP,一个稳定的内部域名,只能在集群内部访问,再结合 k8s ingress-controller 提供对外的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---
apiVersion: v1
kind: Service
metadata:
name: nginx-test
labels:
app: nginx-test
namespace: default
spec:
type: ClusterIP # service 类型,默认 ClusterIP
selector: # 标签选择器
app: nginx-test # 指定关联 Pod 标签
ports:
- port: 80 # service 端口号
protocol: TCP
targetPort: 80 # Pod 内容器端口号

(2)NodePort 节点对外暴露应用

NodePort 在每台 K8S 节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群IP地址。访问地址:<任意NodeIP>:NodePort,默认端口范围:30000-32767

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
apiVersion: v1
kind: Service
metadata:
name: nginx-test
labels:
app: nginx-test
namespace: default
spec:
type: NodePort # service 类型
selector:
app: nginx-test # 指定关联 Pod 标签
ports:
- port: 80 # service 端口
protocol: TCP # 协议类型
targetPort: 80 # 容器内部端口
nodePort: 30011 # 指定在各个node节点暴露的端口号。如果不指定将使用发范围内随机端口号

查看 service

1
2
3
4
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d
nginx-test NodePort 10.97.37.133 <none> 80:30008/TCP 41m

(3)LoadBalance 结合云负载均衡对外暴露应用(公有云)

参考 UCloud UK8S Service

LoadBalance 是通过集群外部的负载均衡设备来暴露服务,负载均衡设备一般由云厂商提供或者使用者自行搭建。

LoadBalance 与 NodePort 类似,也会在每个节点上启用一个端口来暴露服务。区别是云负载均衡器会自动的将 node 节点添加进 RS ,省去了手动添加 Nginx upstream 的步骤。

LoadBalancer 类型的 Service 方法,借助于 Kubernetes 提供的扩展接口,云 K8S 会创建一个与该 Service 对应的负载均衡服务 LB 来承接外部流量,并路由到集群内部。但在诸如微服务等场景下,一个 Service 对应一个负载均衡器,管理成本明显过高,Ingress 因此应运而生。

实际使用需要查看各云厂商提供的文档指引。

(4)ExternalName

将服务映射到一个 DNS 域名(如example.test.ucloud.cn),DNS 域名可通过 spec.externalName 参数配置。

这种类型不常用,这里不做过多赘述。

3.Service 的两种网络代理模式 kube-proxy

Service 的底层主要有 iptables (默认) 和 ipvs 两种网络模式,决定了如何转发流量。k8s自1.8版本开始强推ipvs,之前版本默认使用iptables.

1
2
3
4
5
6
7
8
9
10
# 查看负载均衡规则:
## 1.iptables 模式
$ iptables-save | grep <SERVICE-NAME>

## 2.ipvs 模式 NodePort 上面指定的端口
$ ipvsadm -L -n|grep 30011
TCP 172.17.0.1:30011 rr
TCP 172.25.214.192:30011 rr
TCP 192.168.100.51:30011 rr

4.kubeadm 集群修改 kube-proxy 模式为 IPVS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 查看 kube-proxy
$ kubectl get pods -n kube-system -o wide

# 修改 kube-proxy 的配置文件以 configmap方式存储
$ kubectl edit configmap kube-proxy -n kube-system
....
mode: "ipvs"

# 如果让所有节点生效,需要重建所有节点的 kube-proxy pod
## 逐个删除节点的 kube-proxy,删除完会被自动拉起,并加载最新的 kube-proxy 配置
$ kubectl delete pod kube-proxy-8mmbm -n kube-system # 删除node1的kube-proxy

# 再次验证 node1 的 kube-proxy被拉起
$ kubectl get pods -n kube-system -o wide

# 查看 node1 的 kube-proxy 加载日志
$ kubectl logs kube-proxy-v7gkv -n kube-system
I0419 09:14:57.480897 1 node.go:136] Successfully retrieved node IP: 192.168.99.102
I0419 09:14:57.481117 1 server_others.go:111] kube-proxy node IP is an IPv4 address (192.168.99.102), assume IPv4 operation
I0419 09:14:57.681023 1 server_others.go:259] Using ipvs Proxier. # 启用 ipvs
E0419 09:14:57.706576 1 proxier.go:381] can't set sysctl net/ipv4/vs/conn_reuse_mode, kernel version must be at least 4.1
W0419 09:14:57.706848 1 proxier.go:434] IPVS scheduler not specified, use rr by default
I0419 09:14:57.707188 1 server.go:650] Version: v1.19.0
I0419 09:14:57.707857 1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0419 09:14:57.710100 1 config.go:315] Starting service config controller
I0419 09:14:57.710132 1 shared_informer.go:240] Waiting for caches to sync for service config
I0419 09:14:57.710181 1 config.go:224] Starting endpoint slice config controller
I0419 09:14:57.710193 1 shared_informer.go:240] Waiting for caches to sync for endpoint slice config
I0419 09:14:57.810381 1 shared_informer.go:247] Caches are synced for endpoint slice config
I0419 09:14:57.810482 1 shared_informer.go:247] Caches are synced for service config

# 最后把其他节点的 kube-proxy 杀掉让其启动重启加载新配置
$ kubectl delete pod kube-proxy-4zjr9 -n kube-system
$ kubectl delete pod kube-proxy-g22bf -n kube-system

节点上安装 ipvs 命令行管理工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 去节点安装
$ yum install ipvsadm -y

# 去节点查看规则
$ ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn

......

TCP 10.97.37.133:80 rr # 可以看到一个 serive是转发给后端3个Pod
-> 10.244.36.75:80 Masq 1 0 0
-> 10.244.169.146:80 Masq 1 0 0
-> 10.244.169.147:80 Masq 1 0 0
```
ipvs 会在宿主机上创建一张虚拟网卡 kube-ipvs0,用于接收 K8S 集群内部流量,外部流量也是通过 ipvs 匹配来转发到后端节点的 Pod 上面
```bash
$ ip addr

......

7: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether 9e:58:50:a2:65:19 brd ff:ff:ff:ff:ff:ff
inet 10.96.0.1/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.96.0.10/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.97.37.133/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever

5.二进制 K8S集群修改 kube-proxy 模式为 IPVS:

CoreDNS YAML文件

1
2
3
4
5
6
7
$ vim kube-proxy-config.yml
......
mode: ipvs
ipvs:
scheduler: "rr"

$ systemctl restart kube-proxy

6.kube-proxy模式选择:

kube-proxy是kubernetes中的关键组件,其主要功能是在Service和其后端Pod之间(Endpoint)进行负载均衡。kube-proxy 有三种运行模式,每种都有不同的实现技术:userspace、iptables或者IPVS。

  • userspace模式由于性能问题已经不推荐使用。这里主要介绍iptables和IPVS两种模式的比较及选择。

如何选择

  • 对于集群规模较大,特别是Service数量可能超过1000的,推荐选择IPVS。(详见后续测试数据)

  • 对于集群规模中等,Service数量不多的,推荐选择iptables。

  • 如果客户端会出现大量并发短链接,目前建议选择iptables,原因见下方备注。

备注:在使用IPVS模式的kubernetes集群中进行滚动更新,期间如果有一个客户端在短时间内(两分钟)内发送大量短链接,客户端端口会被复用,导致node收到的来自于该客户端的请求报文网络五元组相同,触发IPVS复用Connection,有可能导致报文被转发到了一个已经销毁的Pod上,导致业务异常。

官方issue:

参考资料1
参考资料2