精通k8s(11)Service实现原理:ClusterIP模式

  • 时间:2025-11-29 21:54 作者: 来源: 阅读:5
  • 扫一扫,手机访问
摘要:为什么需要Service?如果我们写一个程序A,使用Pod部署10个副本组成集群,我们知道这个Pod是动态的,列如说某个Pod重启了,它的地址就变了。前一会你用10.244.234.80这个地址访问,过一会就变成10.244.234.90了,这样谁也无法接受。怎么办呢?我们就想到,弄一个地址不变的总接口,然后让这个接口把流量均衡到各个Pod上面,这样既兼顾了集群,又兼顾了统一,如下所示:这就是se

为什么需要Service?

如果我们写一个程序A,使用Pod部署10个副本组成集群,我们知道这个Pod是动态的,列如说某个Pod重启了,它的地址就变了。前一会你用10.244.234.80这个地址访问,过一会就变成10.244.234.90了,这样谁也无法接受。

怎么办呢?我们就想到,弄一个地址不变的总接口,然后让这个接口把流量均衡到各个Pod上面,这样既兼顾了集群,又兼顾了统一,如下所示:

精通k8s(11)Service实现原理:ClusterIP模式

这就是service的由来。

实验准备

(1)部署
my-go-server-deploy-1.0.yml

第一开启10个my-go-server的副本(程序监听端口9900),使用Deployment来部署。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-go-server
  name: my-go-server
spec:
  replicas: 10   #部署10个副本,可以自定义
  selector:
    matchLabels:
      app: my-go-server
  template:
    metadata:
      labels:
        app: my-go-server
    spec:
      containers:
      - image: crpi-gj5arn39i861t1eu.cn-hangzhou.personal.cr.aliyuncs.com/dev-team-sz/my-go-server:1.0
        name: my-go-server

k8s执行

kubectl apply -f my-go-server-deploy-1.0.yml

2)部署service,文件:
my-go-server-service-clusterIP.yml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: my-go-server
  name: my-go-server
spec:
  selector: #选择pod
    app: my-go-server
  ports:
  - port: 9905
    protocol: TCP
    targetPort: 9900

这个service使用的是简易形式,先将问题简化,后面再一步步复杂。service的拓扑如下:

精通k8s(11)Service实现原理:ClusterIP模式

流程:用户请求service的9905端口,然后service将请求随机的转发到后方的某个Pod。

k8s部署service

kubectl apply -f my-go-server-service-clusterIP.yml

当前Pod的拓扑结构

精通k8s(11)Service实现原理:ClusterIP模式

service部署之后发生了什么?什么是ClusterIP

service部署之后,入口就有了,那么第一就需要为这个入口分配一个IP。k8s会从集群的IP池中取一个分配给service,用于集群内通信,这个IP就称为ClusterIP

我们查看一下实际分配的结果:

# master节点上执行
kubectl get svc my-go-server -o wide

#结果
my-go-server   ClusterIP   10.96.96.199 ...

可以看到是10.96.96.199(由于作者的实验常常重启,这个地址也会变化,只需知道是10.96.xx的样式即可),我们观察到,它和Pod的网络段不一样。拓扑图如下:

精通k8s(11)Service实现原理:ClusterIP模式

第二步:NAT表添加规则

和以往的操作一样,这个ClusterIP也是个虚拟IP,依靠NAT进行逻辑上的转发,下面是NAT的流程图:

精通k8s(11)Service实现原理:ClusterIP模式

观察这个流程,发现也比较简易。这个NAT规则在每个节点机上都有一份。现以node2节点上执行
http://10.96.96.199:9905/user来说明大致的流程:

  1. 出站数据被OUTPUT链拦截,于是依次进入下面的链。
  2. 发现是10.96.96.199的IP,就进入调度链,列如按照依次负载的算法,将地址修改为该Pod的地址,这样就把数据包给劫持了。

通俗的说就是:每部署一个service,NAT就固定写一个到此IP的规则,如果有10个服务,就写10条固定的规则。其次,集群每个节点机上都有一套这样的规则,那么就实现了每台机器上都可以访问service。

我们还记得每个Pod在集群内都有唯一的IP地址,因此调度的时候也不会混乱,直接转发就可以了。

下面看下各个链是怎么写的。

KUBE-SERVICES链

下面的规则就是每增加一个服务,就增加一条这样的固定规则。

-A KUBE-SERVICES -d 10.96.200.158/32 -p tcp -m comment 
--comment "default/my-go-server cluster IP" 
-m tcp --dport 9905 -j KUBE-SVC-MTSFM4LIWSGO5GQC

解释:

  1. -A KUBE-SERVICES:表明在KUBE-SERVICES链上追加一条规则。
  2. -d 10.96.xx:表明匹配的目的IP
  3. -p tcp:表明匹配TCP协议
  4. -m tcp --dport 9905:表明加载tcp模块,匹配9905端口。
  5. -j KUBE-SVC-MTSFM4LIWSGO5GQC:表明跳转到该链上

完整意思是:如果目标IP为10.96.200.158/32且端口等于9905,那么就跳转到KUBE-SVC-MTSFM4LIWSGO5GQC处理。

再看我们发出的请求:
http://10.96.96.199:9905/user,匹配这个规则因此命中。

KUBE-SVC-MTSFM4LIWSGO5GQC链

这条链主要添加调度链,由于我们有10个副本,因此加了十条。

-A KUBE-SVC-MTSFM4LIWSGO5GQC 
-m comment --comment "default/my-go-server" 
-m statistic --mode random --probability 0.10000000009 
-j KUBE-SEP-GZLE4PE5OB5BAAXS
  1. -A KUBE-SVC-MTSFM4LIWSGO5GQC:表明在该链中追加链。
  2. -m statistic --mode random --probability 0.10000000009:-m表明加载 statistic 扩展模块,用于实现基于概率的流量匹配。
    --mode random表明概率模式为随机。
    --probability 0.10000000009表明概率可能性为10%左右。
  3. -j KUBE-SEP-GZLE4PE5OB5BAAXS:表明有10%的概率会跳到KUBE-SEP-GZLE4PE5OB5BAAXS链上进行处理。

完整意思是:有10%的概率会命中KUBE-SEP-GZLE4PE5OB5BAAXS链进行处理

下面再看下KUBE-SEP-GZLE4PE5OB5BAAXS链。

KUBE-SEP-GZLE4PE5OB5BAAXS链

主要有两条链组成:

#第一条
-A KUBE-SEP-GZLE4PE5OB5BAAXS 
-s 10.244.169.183/32 
-m comment --comment "default/my-go-server" 
-j KUBE-MARK-MASQ

#第二条
-A KUBE-SEP-GZLE4PE5OB5BAAXS 
-p tcp -m comment --comment "default/my-go-server" 
-m tcp -j DNAT --to-destination 10.244.169.183:9900

由于和前面的规则很类似,这里简化写一下:

第一条:如果是从10.244.169.183/32里面发出的,就将它标记一下,后续进行SNAT转换。

第二条:将地址进行DNAT转换,目标地址改为10.244.169.183:9900(最终目标达成!)。

为什么要进行SNAT转换呢?看下拓扑图:

精通k8s(11)Service实现原理:ClusterIP模式

如图所示的小红人,如果我们就在容器内部执行:
http://10.96.96.199:9905/user,那么就是Pod的IP,因此要进行SNAT转换,不然数据回不来了。

像上面样的规则一共有10对,由于我们有10个副本。

kube-proxy创建iptables规则

在工作节点中,我们前面写了kube-node容器,另外还有一个就是kube-proxy容器。

精通k8s(11)Service实现原理:ClusterIP模式

kube-proxy会监控service资源的变化,进而生成iptables的规则。列如删除service时,kube-proxy会清理NAT表中的相关规则。这里就不再详写。

实验结束的清理工作

#删除service
kubectl delete svc my-go-server

#删除deploy
kubectl delete deploy my-go-server

#删除部署文件
rm -rf my-go-server-service-clusterIP.yml

完整总结

这篇详细分析了services实现的细节,下面是整个过程:

  1. 当创建services时,需要为这个service安排一个IP,也就是访问IP(ClusterIP),如果不指定k8s会生成一个,样式为:10.96.xx
  2. services部署之后,kube-proxy会监控到这个变化,然后在NAT表中生成一系列的规则,这是保证services工作的关键。
  3. 具体是:
    (1)在每个节点机上都生成一套一样的NAT规则,保证集群中的任何位置都可以访问services。
    (2)对入站和出站且目标IP为services IP的请求进行DNAT转换,通过概率算法(也可以有别的)转发到某一个具体Pod,由于Pod在集群中拥有唯一的IP,因此不会混乱。
    (3)如果从Pod内部发出的请求,就执行SNAT转换。由于外部不知道容器,SNAT可以让响应准确的回来。
  4. 最后一步:转换成Pod IP之后,就回到前面我们讨论的Pod如何通信的问题了,也就是通过容器底层网络进行通信。

需要注意的是,ClusterIP是集群内部IP,只在集群内部使用

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部