参考

预置知识

  • 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

  • 容器化技术(十四):Kubernetes中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
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐