[Kubernetes]Kubernetes的网络模型

Kubernetes的网络模型从内至外由四个部分组成:

  1. Pod内部容器所在的网络
  2. Pod所在的网络
  3. Pod和Service之间通信的网络
  4. 外界与Service之间通信的网络

建议在阅读本文之前先了解Docker的网络模型。可以参看作者的前两篇文章[Kubernetes]Docker的网络模型[Kubernetes]Docker的overlay网络模型

1. Pod内部容器所在的网络和Pod所在的网络

Kubernetes使用了一种“IP-per-pod”网络模型:为每一个Pod分配了一个IP地址,Pod内部的容器共享Pod的网络空间,即它们共享Pod的网卡和IP。Kubernetes是怎么做的呢?聪明的读者一定想到了[Kubernetes]Docker的网络模型里介绍的Docker的container网络。Kubernetes在创建Pod时,先在Node节点的Docker上创建了一个运行在bridge网络的“pod容器”,为这个pod容器创建虚拟网卡eth0并分配IP地址。而Pod里的容器(称为app容器),在创建时使用--net=container:来共享pod容器的网络空间。

例如,笔者手里有一个Kubernetes Node主机PT-169124, IP地址为192.168.169.124. 笔者的环境用flannel作为了Docker的bridge网络驱动,用ifconfig命令可以看到flannel网络设备:

flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 172.17.17.0  netmask 255.255.0.0  destination 172.17.17.0
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
        RX packets 7988  bytes 410094 (400.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 12681  bytes 17640033 (16.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

这个flannel0是Kubelet用flannel创建出来的bridge,并在Docker进程启动时,作为了--bridge参数值:--bridge=flannel0,这样使得Docker里创建的容器默认都使用flannel0网段里的IP地址。查看这个主机上运行的Docker容器(受限于格式,这里只显示容器的ID和名字):

# docker ps
CONTAINER ID       NAMES
7672d97e01d1       k8s_kubernetes-dashboard.979fa630_kubernetes-dashboard-4164430742-lqhcg_kube-system_a08a0d66-57bf-11e6-84b8-5cf3fcba84a8_086f7305
431338595af6       k8s_POD.42430ae1_kubernetes-dashboard-4164430742-lqhcg_kube-system_a08a0d66-57bf-11e6-84b8-5cf3fcba84a8_e96d8681

这两个容器即是作者在[Kubernetes]Kubernetes集群和Docker私有库搭建(CentOS 7)一文里搭建的kubernetes-dashboard所创建出来的pod容器和app容器。其中pod容器就运行在由flannel驱动的bridge网络里:

# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "6031ecf132905a10b6500a6911d914aff2e15ca8894225aa59ca34bf965b902e",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.17.1/24",
                    "Gateway": "172.17.17.1"
                }
            ]
        },
        "Containers": {
            "431338595af6236a3feb661129e17a4aed4d8331c173903bc2aa7a788d494c6d": {
                "Name": "k8s_POD.42430ae1_kubernetes-dashboard-4164430742-lqhcg_kube-system_a08a0d66-57bf-11e6-84b8-5cf3fcba84a8_e96d8681",
                "EndpointID": "6f22b9c24be10bf069973e0ba651efafdc68e13177e9cbbe3f41b6b4e963eff1",
                "MacAddress": "02:42:ac:11:11:03",
                "IPv4Address": "172.17.17.3/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1472"
        }
    }
]

再用docker inspect来查看kubernetes-dashboard app容器的详细信息,能看到NetworkMode值为 "container:431338595af6236a3feb661129e17a4aed4d8331c173903bc2aa7a788d494c6d",说明app容器共享pod容器的网络空间,即它们有相同的网卡和IP地址。

Kubernetes这种“IP-per-pod”网络模型,能让Pod里的容器相互之间通过loopback(127.0.0.1, localhost)网络访问,同时也意味着Pod里的容器在端口分配上不能发生冲突,而Pod里的容器根本不用担心和其他Pod里的容器发生端口冲突。

更有意思的是,Kubernetes把各Node主机上的Docker的bridge网络“外包”给了flannel,flannel在这些主机上创建的flannel0网络使用的是同一个子网的不同区间。例如本文的环境里,flannel在两台Node主机上创建的flannel0网络分别是:

主机PT-169121, IP地址为192.168.169.121:

flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 172.17.13.0  netmask 255.255.0.0  destination 172.17.13.0
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
        RX packets 12684  bytes 17640285 (16.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7991  bytes 410346 (400.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

主机PT-169124, IP地址为192.168.169.124:

flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 172.17.17.0  netmask 255.255.0.0  destination 172.17.17.0
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
        RX packets 7988  bytes 410094 (400.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 12681  bytes 17640033 (16.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

flannel通过etcd,将各Node主机上的Docker bridge网络联合了起来,实现了:

  • 所有的容器都能相互通信,不管容器是否运行在同一个Node主机上
  • 所有的容器和Node主机都能相互通信,不管容器是否运行在该Node主机上。

2. Pod和Service之间通信的网络

Kubernetes里的Pod不是一个“长生”的家伙,它会由于各种原因被销毁和创造。比如在垂直扩容和滚动更新过程中,旧的Pod会被销毁,被新的Pod代替。这期间,Pod的IP地址甚至会发生变化。所以Kubernetes引进了Service.Service是一个抽象的实体,Kubernetes在创建Service实体时,为其分配了一个虚拟的IP,当我们需要访问Pod里的容器提供的功能时,我们不直接使用Pod的IP地址和端口,而是访问Service的这个虚拟IP和端口,由Service把请求转发给它背后的Pod.

Kubernetes在创建Service时,根据Service的标签选择器(Label Selector)来查找Pod,据此创建与Service同名的EndPoints对象。当Pod的地址发生变化时,EndPoints也随之变化。Service接受到请求时,就能通过EndPoints找到请求转发的目标地址。

例如,作者在搭建kubernetes-dashboard是创建出的服务如下图所示,背后有两个Pod在支撑。

通过kubectl get命令分别查看一下Service和EndPoints的信息:

# kubectl get services/kubernetes-dashboard --namespace=kube-system -o yaml

apiVersion: v1

kind: Service

metadata:

  creationTimestamp: 2016-08-01T08:12:02Z

  labels:

    app: kubernetes-dashboard

  name: kubernetes-dashboard

  namespace: kube-system

  resourceVersion: "18293"

  selfLink: /api/v1/namespaces/kube-system/services/kubernetes-dashboard

  uid: a0953fa0-57bf-11e6-84b8-5cf3fcba84a8

spec:

  clusterIP: 10.254.213.209

  portalIP: 10.254.213.209

  ports:

  - nodePort: 31482

    port: 80

    protocol: TCP

    targetPort: 9090

  selector:

    app: kubernetes-dashboard

  sessionAffinity: None

  type: NodePort

status:

  loadBalancer: {}
# kubectl describe endpoints/kubernetes-dashboard --namespace=kube-system
Name:        kubernetes-dashboard
Namespace:    kube-system
Labels:        app=kubernetes-dashboard
Subsets:
  Addresses:        172.17.13.2,172.17.17.3
  NotReadyAddresses:    <none>
  Ports:
    Name    Port    Protocol
    ----    ----    --------
    <unset>    9090    TCP

No events.

可以看到,Service的targetPort和Pod的IP地址都记录在了与Service同名的EndPoints里。我们再深入看看当通过Service的虚拟IP和端口访问Service时,请求是如何到达Pod的。

前面说过,Service只是一个虚拟的实体,真正完成请求转发的是运行在Node节点上的kube-proxy.Service的虚拟IP就是由kube-proxy实现的。kube-proxy有两种请求转发模式:userspace模式和iptables模式。在Kubernetes v1.1版本之前默认是userspace模式,v1.2版本后默认是iptables模式。

userspace模式:当创建Service时,所有Node节点上的kube-proxy都会在其所在的本地节点上随机开放一个端口(称为代理端口),然后建立一个iptables规则(一种linux包处理逻辑),iptables会完成<服务的虚拟IP, 端口>与代理端口的流量转发,再从EndPoints里选择一个Pod,把代理端口的流量转给该Pod. 当EndPoints下有多个Pod时,选择Pod的算法有两种:1 依次循环,如果一个Pod没有响应,就试下一个(service.spec.sessionAffinity值为"None");2 选择与请求来源IP更接近的Pod(service.spec.sessionAffinity值为"ClientIP").

userspace模式的缺点是:只适用于规模较小的集群;会对Pod屏蔽请求的source IP,使得部分基于source IP的防火墙失效。

iptables模式:当创建Service时,所有Node节点上的kube-proxy都会建立两级iptables规则,一级为Service创建,目的是将<服务虚拟IP,端口>的流量转给后端,另一级为EndPoints创建,目的是用于选择Pod. 当service.spec.sessionAffinity值为"ClientIP"时,iptables模式选择Pod的算法和userspace模式相同。当service.spec.sessionAffinity值为"None"时,随机选择Pod,所以如果被选择的Pod没有响应,不会尝试选择另一个Pod.

例如,作者的Kubernetes环境是v1.2版本,用iptables -vL --line-numbers -t nat命令能看到创建的iptables规则:

kube-proxy为kubernetes-dashboard服务创建的iptables规则:

kube-proxy为kubernetes-dashboard EndPoints创建的iptables规则:

iptables模式相比userspace模式更快更稳定,也不存在请求的source IP的问题。

我们介绍了Kubernetes是如何用Service来“代理”Pod,那么被创建的Service如何被其他Pod感知到呢?比如说,一个前端的Pod如何找到后端的Service?Kubernetes提供了两种方法:环境变量和DNS.这部分的内容,Kubernetes的官网有所介绍。

3. 外界与Service之间通信的网络

如果想把服务暴露给外界(为什么不直接暴露Pod呢?相信读者自己有答案。),有三种方式:

Service的类型

如果不指定Service的spec.type的值,创建的Service的类型默认为ClusterIP类型。这种类型的Service只会得到虚拟的IP和端口,只能在Kubernetes集群内部被访问。

如果指定Service的spec.type的值为“NodePort”,创建的Service的类型默认为NodePort类型。这种类型的Service除了会得到虚拟的IP和端口,Kubernetes还会在所有Node节点上为其分配端口。分配的端口的值可以通过spec.ports[*].nodePort指定,或由Knubernetes在配置好的区间里分配(默认为30000-32767)。这种Service即可以从Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort访问,例如,作者创建的kubernetes-dashboard服务就是NodePort类型,Kubernetes为其分配的nodePort为31482,通过任意节点的IP(192.168.169.121或192.168.169.124)都能访问。

如果指定Service的spec.type的值为“LoadBalancer”,创建的Service的类型默认为LoadBalancer类型。这种类型的Service除了会得到虚拟的IP和端口,Kubernetes还会在所有Node节点上为其分配端口,然后为其开通负载均衡。这种Service即可以从Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort访问,还可以通过负载均衡的IP访问。

绑定外部IP

通过指定Service的spec.externalIPs[*],可以将Service暴露在节点的外部IP上。外界通过这个IP和Service的端口可以访问到该Service.

使用Ingress

先参见Kubernetes官方文档。待作者亲手完成环境验证后补上。

时间: 2024-10-18 12:09:28

[Kubernetes]Kubernetes的网络模型的相关文章

[Kuberentes][Kubernetes容器网络3]kubernetes 纯三层网络模型

目录 解读 Kubernetes 三层网络方案 Flannel Host Gateway 模式 Calico 项目 Calico 项目的架构 核心流程 Route Reflector模式 总结 解读 Kubernetes 三层网络方案 除了前面说的 VXLAN 这种二层虚拟可扩展局域网方案之外,还有纯三层网络方案. Flannel Host Gateway 模式 Flannel 的 Host Gateway 模式: Flannel在宿主机上创建如下路由规则: $ ip route ... 10.

[Kubernetes][Kubernetes容器网络2]深入解析容器跨主机网络

目录 深入解析容器跨主机网络 Flannel Flannel UDP模式基本原理 UDP模式的缺陷 Flannel VXLAN 模式 深入解析容器跨主机网络 在Docker默认配置下,不同宿主机上的容器通过IP地址是无法相互通信的. 因此社区出现了很多用于解决容器跨主机通信问题的方案. Flannel Flannel 支持三种后端实现: VXLAN host-gw UDP 先以 UDP 模式为例 Flannel UDP模式基本原理 假设有两台宿主机: Node1: container-1,IP

盘点Kubernetes网络问题的4种解决方案

由于在企业中部署私有云的场景会更普遍,所以在私有云中运行Kubernetes + Docker集群之前,就需要自己搭建符合Kubernetes要求的网络环境.现在的开源世界里,有很多开源组件可以帮助我们打通Docker容器和容器之间的网络,实现Kubernetes要求的网络模型.当然每种方案都有自己适合的场景,我们要根据自己的实际需要进行选择. 一.Kubernetes + Flannel Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中,这在GCE(Goog

kubernetes学习01—kubernetes介绍

一.简介 1.Kubernetes代码托管在GitHub上:https://github.com/kubernetes/kubernetes/. 2.Kubernetes是一个开源的,容器集群管理系统,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制.通过Kubernetes你可以: 快速部署应用 快速扩展应用 无缝对接新的应用功能 节省资源,优化硬件资源的使用 3.Kubernetes一个核心的特点就

从零开始入门 K8s | Kubernetes 网络概念及策略控制

作者 |?阿里巴巴高级技术专家? 叶磊 一.Kubernetes 基本网络模型 本文来介绍一下 Kubernetes 对网络模型的一些想法.大家知道 Kubernetes 对于网络具体实现方案,没有什么限制,也没有给出特别好的参考案例.Kubernetes 对一个容器网络是否合格做出了限制,也就是 Kubernetes 的容器网络模型.可以把它归结为约法三章和四大目标. 约法三章的意思是:在评价一个容器网络或者设计容器网络的时候,它的准入条件.它需要满足哪三条? 才能认为它是一个合格的网络方案.

How-to setup Kubernetes to manage Docker Cluster on ubuntu

什么是 Kubernetes Kubernetes 是来自 Google 云平台的开源容器集群管理系统.基于 Docker 构建一个容器的调度服务.该系统可以自动在一个容器集群中选择一个工作容器供使用.其核心概念是 Container Pod.详细的设计思路请参考这里. 关于 Kubernetes 系统架构及组件介绍见这里. 本文通过实际操作来演示Kubernetes的使用,主要包括如下内容: 部署环境介绍,以及Kubernetes集群逻辑架构 安装部署Open vSwitch跨机器容器通信工具

CoreOS Linux引入了Kubernetes kubelet

CoreOS Linux引入了Kubernetes kubelet 作者:Kelsey Hightower 2015年8月14日 这周我们在 CoreOS Linux 的 alpha 开发版集成了 kubelet——Kubernetes 的一个核心内嵌组件.Kubelet 负责维护 pod(应用实例)集合.Pod 集合由本地系统的一个或多个容器构成.在 Kubernetes 集群中,kubelet 用于作为本地代理,通过访问 Kubernetes 的 API 服务器,监控 PodSpecs 的状

kubernetes部署之master高可用(3)

部署的Master节点集群由k8s-master01, k8s-master02, k8s-master03三个节点组成,每个节点上部署kube-apiserver,kube-controller-manager,kube-scheduler三个核心组件. kube-apiserver的3个实例同时提供服务,在其前端部署一个高可用的负载均衡器作为kube-apiserver的地址. kube-controller-manager和kube-scheduler也是各自3个实例,在同一时刻只能有1个

Ubuntu 16.04下搭建kubernetes集群环境

简介 目前Kubernetes为Ubuntu提供的kube-up脚本,不支持15.10以及16.04这两个使用systemd作为init系统的版本. 这里详细介绍一下如何以非Docker方式在Ubuntu16.04集群上手动安装部署Kubernetes的过程. 手动的部署过程,可以很容易写成自动部署的脚本.同时了解整个部署过程,对深入理解Kubernetes的架构及各功能模块也会很有帮助. 环境信息 版本信息 组件 版本 etcd 2.3.1 Flannel 0.5.5 Kubernetes 1