如何通过service访问pod

816 阅读4分钟

如何通过service访问pod

​ 我们知道对于Pod而言其IP是不固定的,因为若该Pod发生故障时会立即被新的Pod代替,此时会分配新的IP,注意这些IP是虚拟IP,但是思考一下Pod对外提供服务,是怎么在IP变化的情况下,还能提供完善的功能呢?

​ 答案是通过Service

一、创建Service

我们案例使用之前介绍K8s框架使用deployment控制器创建对外服务

在之前案例的基础上,我们增加service资源(service.yaml)

# service的版本
apiVersion: v1
# 资源类型
kind: Service
# service的命名空间以及名字
metadata:
  namespace: k8s-test
  name: service
spec:
  # 将service8080端口映射到30000端口
  ports:
    - name: "service-port"
      targetPort: 8080
      port: 8080
      nodePort: 30000
  # 指明label为service-test的pod为service的后端
  selector:
    app: service-test
  # 暴露给外网
  type: NodePort

执行

kubectl apply -f service.yaml

查看service服务

[centos@wunaichi k8stest]$ kubectl get service -n k8s-test
NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service   NodePort   10.111.35.139   <none>        8080:30000/TCP   18d

service分配到CLUSTER-IP为10.111.35.139,端口映射为8080:30000

查看service与pod的关系

[centos@wunaichi k8stest]$ kubectl describe service service -n k8s-test
Name:                     service
Namespace:                k8s-test
Labels:                   <none>
Annotations:              <none>
Selector:                 app=service-test
Type:                     NodePort
IP:                       10.111.35.139
Port:                     service-port  8080/TCP
TargetPort:               8080/TCP
NodePort:                 service-port  30000/TCP
Endpoints:                10.44.0.3:8080,10.44.0.4:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

其中Endpoints罗列了2个Pod的IP和端口,我们知道Pod的IP是在容器中配置的,那么Service的ClusterIP又是配置在哪里的呢?CLUSTER-IP又是如何映射到Pod IP的呢?

答案是kube-proxy+iptables

二、Service的工作原理

Service是由kube-proxy组件和iptables组成。iptables规则将service的ip映射到pod的ip.

查看机器的iptables

sudo iptables-save

我们截取关键部分进行解读

第一条:宿主机访问service,对于原地址非10.24.0.0/16,目的地址为10.111.35.139,则跳转到KUBE-MARK-MASQ( 这里跳转到KUBE-MARK-MASQ是为了包出宿主机时,其ip是宿主机ip),不怎么重要,重点看第二条

第二条:凡是目的地是10.111.35.139(service的ip)端口是8080都要跳转到KUBE-SVC-IUAUJYFCIUUMOL2O这条规则

我们继续跳转。 KUBE-SVC-IUAUJYFCIUUMOL2O以50%的概率跳转到KUBE-SEP-3C4G3TP5TJKPOH3O,以50%的概率跳转到KUBE-SEP-2ATDMDQBZCO4RJUM

以跳转到KUBE-SEP-3C4G3TP5TJKPOH3O为例

可以看到KUBE-SEP-3C4G3TP5TJKPOH3O将目的地址通过DNAT转到10.44.0.3:8080, 而这个ip正是其中一个pod的ip

这里DNAT的作用就是把流入的IP和端口改成新的端口和地址,也就是被代理的Pod的IP和端口

到此,我们把service的请求已经交给对应的pod来进行处理

所以iptables规则将访问Service的流量会以相同概率转发到后端Pod,而且使用类似轮询的负载均衡策略。而这些规则正是kube-proxy监听Pod的变化事件,在宿主机上生成并维护的。

kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Pod 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP`和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。

可以看到,kube-proxy 通过 iptables 处理 Service 的过程,其实需要在宿主机上设置相当多的 iptables 规则。而且,kube-proxy 还需要在控制循环里不断地刷新这些规则来确保它们始终是正确的

三、外网如何访问service

除了cluster内部访问service,很多情况下我们也希望可以将service暴露到外部

service通过cluster内部的IP对外提供服务,但是只有cluster节点以及Pod才可以访问。那外网怎么访问service?那就是NodePort。我们再回看一下第一节的yaml文件

# service的版本
apiVersion: v1
# 资源类型
kind: Service
# service的命名空间以及名字
metadata:
  namespace: k8s-test
  name: service
spec:
  # 将service8080端口映射到30000端口
  ports:
    - name: "service-port"
      # pod监听的端口
      targetPort: 8080
      # cluster监听的端口
      port: 8080
      # 节点上监听的端口
      nodePort: 30000
  # 指明label为service-test的pod为service的后端
  selector:
    app: service-test
  # 暴露给外网
  type: NodePort

可以看到 type: NodePort ;且含有节点监听的端口30000

最终,Node和ClusterIP在各自端口上接收到的请求都会通过iptables转发到Pod的targetPort

可以看到对外端口已经生效

我们再看看一下iptables规则

可以看到监听30000端口,该端口的请求都会跳转到KUBE-SVC-IUAUJYFCIUUMOL2O,而KUBE-SVC-IUAUJYFCIUUMOL2O同cluster一样均会按照相同比例跳转到某个Pod上面。

至此关于外部如何访问Pod的问题应该很清楚了吧

引用

kubernetes官方文档-服务

《每天5分钟玩转kubernetes》