Kubernetes(k8s)Service 与 kube-proxy 运行关系分析
参考详解 Kubernetes Service 的实现原理Service JimmysongService 官方文档kubernetes实践之四十八:Service Controller与Endpoint Controllerk8s基础学习—endpoint,服务与pod之间的中介容器化技术(十四):Kubernetes中Headless ServiceK8s 普通Service和Headless
·
文章目录
参考
预置知识
- Service
- 门口的侍客,承接客人的所有要求
- k8s 的集群内服务访问
- ClusterIP 类型,集群内虚拟 IP
- None 类型,headless,不会创建 Cluster IP,依靠 DNS 解析服务,进行服务访问
- Pod
- 真正干活的人,产出者,接触不到客人
- 业务容器
- Endpoints
- 管理者,接触不到客人
- 管理 Pod,内容一组 IP 列表(是 Service 对应的 一组 Pod 的 IP)
- 当 Service 被删除时,Endpoints Controller 会将对应的 Pod 删除
- Kube-proxy
- 大堂联络人,建立侍客与干活人的直接联系,接触不到客人
- 具有负载均衡模块,建立 iptables 或 ipvs 规则,使客人的要求从侍客直达干活人
- 创建过程
- 首先 Service 和 Pod 创建
- Endpoints Controller 监听到他们的创建,创建对应的 Endpoints 对象
- 之后 kube-proxy 进程会监控 Service 和 Endpoints 的更新,并调用其 Load Balancer 模块在主机上刷新路由转发规则
iptables
- iptables 的底层实现是 netfilter
- 作为一个通用的、抽象的框架,提供一整套 hook 函数的管理机制,使得数据包过滤、包处理(设置标志位,修改 TTL 等)、地址伪装、网络地址转换、透明代理、访问控制、基于协议类型的连接跟踪,甚至带宽限速等成为可能
- netfilter 的架构就是在整个网络流程的若干位置防止一些钩子,并在每个钩子上挂载一些处理函数进行处理
- iptables 是用户空间的一个程序,通过 netlink 和内核的 netfilter 框架打交道,负责往钩子上配置回调函数
五链五表
五链 可添加自定义链
- INPUT 链:用于处理输入本地进程的数据包
- OUTPUT 链:用于处理本地进程的输出数据包
- FORWARD 链:用于处理转发到其他机器 network namespace 的数据包
- PREROUTING 链:可以在此处进行 DNAT
- POSTROUTING 链:可以在此处进行 SNAT
- 除了上面系统预定义的 5 条 iptables 链,用户还可以在表中定义自己的链
五表 不支持添加自定义表
- filter 表:用于控制到达某条链上的数据包是继续放行、直接丢弃 drop、或拒绝 reject
- nat 表:用于修改数据包的源和目的地址
- mangle 表:用于修改数据包的 IP 头信息
- raw 表:iptables 是有状态的,即 iptables 对数据包有连接追踪 connection tracking 机制,而 raw 是用来去除这种追踪机制的
- security 表:最不常用的表(通常,我们说 iptables 只有 4 张表,security 表是新加的特性),用于在数据包上应用 SELinux
Service
普通 Service 与 headless Service
-
Service 会与同名的 Endpoints 自动进行关联
-
type: ClusterIP
- 普通服务
- 具有 ClusterIP(集群内虚拟IP)
-
type: None
- 无头服务
- 不具有 ClusterIP
- 需要自行创建同名 Endpoints,自动关联
# 普通service
# 下面就是说访问 Service 就会 访问到对应容器的 9376端口
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP # 此Service类型为ClusterIP
selector:
app: MyApp
ports:
- protocol: TCP
port: 80 # service 端口
targetPort: 9376 # 容器端口
-----
# 无头service(headless service)
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: None # 此Service类型为ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
headless Service
-
具有 selector
- 创建 Deployment 等资源,产生的 Endpoints,会与该 Service 自动关联
- 此种不使用 kube-proxy 负载均衡机制
- 使用 DNS,可利用 DNS 负载均衡机制,随机选择一个 Pod 或 选择某一个 Pod
# type: ClusterIP
# clusterIP: None
apiVersion: v1
kind: Service
metadata:
name: nginx-service-headless
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
clusterIP: None # 无头服务
selector:
app: nginx # 定义此Service关联的Pod对应的标签
type: ClusterIP # 此Service类型为ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels: # 定义Deployment关联的Pod的标签
app: nginx
replicas: 3 # 告诉Deployment运行的Pod实例数目
template: # Pod的模板
metadata:
labels:
app: nginx # Pod的标签
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
------------
# 创建完成后
# 随机登录到一个Pod中,查看的DNS记录情况,结果如图所示,会显示后面的所有的关联的Pod的IP,需要用户根据情况,从中选择要访问的具体Pod
sh-4.4# nslookup nginx-service-headless
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: nginx-service-headless.default.svc.cluster.local
Address: 10.244.1.51
Name: nginx-service-headless.default.svc.cluster.local
Address: 10.244.2.20
Name: nginx-service-headless.default.svc.cluster.local
Address: 10.244.1.52
- 不具有 selector
- 此种后创建 Endpoints,可配置集群外部 IP 用于访问
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
clusterIP: None # 无头服务
type: ClusterIP # 此Service类型为ClusterIP
-------
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 10.20.40.150 # 集群外部 IP
ports:
- port: 80
kube-proxy
- 运行在每个节点上,监控着 Service 和 Endpoints 的更新,并调用其 LoadBalancer 模块在主机上刷新路由转发规则
- 每个 Pod 都可以通过 Liveness Probe 和 Readiness probe 探针进行健康检查
- 若有 Pod 处于未就绪状态,则 kube-proxy 会删除对应的转发规则
- kube-proxy 的 Load Balancer 模块并不是很智能,如果转发的 Pod 不能正常提供服务,它不会重试或尝试连接其他的 Pod
userspace 模式
- 此模式是通过 kube-proxy 用户态程序实现 Load Balancer 的代理服务
- kube-proxy 1.0 之前的默认模式,现在较少使用
- 由于转发发生在用户态,因此效率不高,且容器丢包
- 当内核版本不高时,适用此模式,因为 iptables 和 ipvs 都要求高版本内核
原理
- 访问服务的请求到达节点后首先会进入内核 iptables,然后回到用户空间,由 kube-proxy 完成后端 Pod 的选择,并建立一条到后端 Pod 的连接,完成代理转发的工作
- 因此这样流量从用户空间进出内核将带来不小的性能损耗
为什么 userspace 模式要建立 iptables规则?
- 因为 kube-proxy 进程只监听一个端口
- 这个端口并不是服务的访问端口,也不是服务的 Nodeport
- 因此需要一层 iptables 把访问服务的连接重定向给 kube-proxy 进程的一个临时端口
- 该临时端口,用于承接后端 Pod 的响应,返回给 kube-proxy,然后 kube-proxy 再返回给客户端
- Nodeport 工作原理同 Cluster IP,发送到 Node 指定端口的数据,通过 iptables 重定向到 kube-proxy 对应的端口上,然后 kube-proxy 把数据发送到其中一个 Pod 上
iptables 模式
- iptables 是用户空间应用程序,通过配置 Netfilter 规则表(Xtables)构建 Linux 内核防火墙
- 与 userspace 模式最大的不同是:kube-proxy 利用 iptables 的 DNAT 模块,实现了 Service 入口地址到 Pod 实际地址的转换,免去一次内核态到用户态的切换
- 既然利用了 iptables 做了一次 DNAT,为了保证回程报文能够顺利返回,需要做一次 SNAT,kube-proxy 也要创建相应的 iptables 规则
ipvs 模式
- 也基于 netfilter 模块,但比 iptables 性能更高,具备更好的可扩展性
- 随着集群规模的增长,资源的可扩展性标的越来越重要
- iptables 难以扩展到支持成千上万的服务,纯粹为防火墙设计的
- iptables 底层路由表的实现是链表,对于路由规则的增删改查操作都涉及遍历一次链表,低效
- ipvs 专门用于负载均衡,采用更高效的数据结构(散列表),允许几乎无限的规模扩张
工作原理
- 外来的数据包首先经过 netfilter 的 PREOUTING 链,然后经过一次路由决策到达 INPUT 链,再做一次 DNAT 后,经过 FORWARD 链离开本机网路协议栈
- 由于 IPVS 的 DNAT 发生在 netfilter 的 INPUT 链,因此如何让网路报文经过 INPUT 链在 IPVS 中就变得十分重要了,一般有两种解决方法:
- 第一种方法,把服务的虚 IP 写到本机的本地内核路由表中
- 第二种方法,在本机创建一个 dummy 网卡,然后把服务的虚 IP 绑定到该网卡上
负载均衡模式
DR
- 此模式应用最广泛
- 工作在 L2,即通过 MAC 地址做 LB,而非 IP 地址
- 此模式,回程报文不会经过 IPVS Director 而是直接返回客户端
- 此种模式下要求 IPVS 的 Director 和客户端在同一个局域网内
- 此种模式不支持端口映射,无法支撑 k8s 的所有场景
Tunneling
- 此模式就是用 IP 包封装 IP 包,因此也称为 ipip 模式
- 此模式,回程报文不会经过 IPVS Director 而是直接返回客户端
- 同样不支持端口映射,因此很难被用在 k8s 的 Service 场景中
NAT
- 支持端口映射
- 回程报文需要经过 IPVS Director,因此也称 Masq(伪装)模式
- k8s 实现 Service 时用的正是此模式
IPVS 模式中的 iptables 和 ipset
- IPVS 用于流量转发,无法处理 kube-proxy 中的其他问题,如包过滤、SNAT 等
- IPVS 模式的 kube-proxy 将在以下四种情况依赖 iptables:
- kube-proxy 配置启动参数 masquerade-all = true,即集群中所有经过 kube-proxy 的包都做一次 SNAT
- kube-proxy 启动参数指定集群 IP 地址范围
- 支持 Load Balancer 类型的服务,用于配置白名单
- 支持 Nodeport 类型的服务,用于在包跨节点前配置 MADQUERADE,类似与上文提到的 iptables 模式
- 因为不想创建太多的 iptables 规则,因此使用 ipset 减少 iptables 规则,使得不管集群内有多少服务,IPVS 模式下的 iptables 规则的总数在 5 条以内
小节
- kube-proxy 实现的是分布式负载均衡,而非集中式负载均衡
- 何为分布式负载均衡?
- 就是每个节点都充当一个负载均衡器,每个节点上都会配置一模一样的转发规则
Ingress
-
七层负载均衡,利用不同的 path 路径来访问不同的服务,实现负载均衡
-
为什么要发明 Ingress 这个概念呢?
- 其一重要原因:为了便于服务动态发现和负载均衡
- 在微服务中,外部网络要通过域名、UR 路径、负载均衡等转发到后端私有网络中
- 微服务之所以成为”微“,是因为它是动态变化的,经常会被增加、删除、更新
-
传统的反向代理对服务动态变化的支持不是很方便
- 就是服务变更后,不是很容易马上改变配置和热加载(就是改变后,不需要手动重载,直接变更指向了新服务后端)
-
Nginx Ingress Controller 的出现就是为了解决这个问题
- 它可以时刻监听服务注册和服务编排 API ,随时感知后端服务的变化,自动重新更改配置并重新加载
- 这期间服务不会暂停或停止,这对用户来说是无感知的
-
Nginx Ingress Controller 就是为了与 k8s 交互,同时刷新 Nginx 配置,还能重载 Nginx
-
号称云原生边界路由的 Traefik 设计得更彻底,首先是个反向代理,其次原生提供了对 k8s 的支持
- 就是使用时,不需要再开发一套 Nginx Ingress Controller
更多推荐
已为社区贡献41条内容
所有评论(0)