Kubernetes设计文档 网络介绍

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 模型和动机 Kubernetes从Docker默认的网络模型中独立出来形成一套自己的网络模型。该网络模型的目标是:每一个pod都拥有一个扁平化共享网络命名空间的IP,通过该IP,pod就能够跨网络与其它物理机和容器进行通信。

模型和动机

Kubernetes从Docker默认的网络模型中独立出来形成一套自己的网络模型。该网络模型 的目标是:每一个pod都拥有一个扁平化共享网络命名空间的IP,通过该IP,pod就能够跨网络与其它物理机和容器进行通信。一个pod一个IP模型创建了一个干净、反向兼容的模型,在该模型中,从端口分配、网络、域名解析、服务发现、负载均衡、应用配置和迁移等角度,pod都能够被看成虚拟机或物理机。

另一方面,动态端口分配需要以下方面的支持:

  1. 固定端口(例如:用于外部可访问服务)和动态分配端口;
  2. 分割集中分配和本地获取的动态端口; 不过,这不但使调度复杂化(因为端口是一种稀缺资源),而且应用程序的配置也将变得复杂,具体表现为端口冲突、重用和耗尽;
  3. 使用非标准方法进行域名解析(例如:etcd而不是DNS);
  4. 对使用标准域名/地址解析机制的程序(例如:web浏览器)使用代理和/或重定向;
  5. 除了监控和缓存实例的非法地址/端口变化外,还要监控用户组成员变化以及阻止容器/pod迁移(例如:使用CRIU)。

NAT将地址空间分段的做法引入了额外的复杂性,这将带来诸如破坏自注册机制等问题。

在一个pod一个IP模型中,从网络角度看,在一个pod中的所有用户容器都像是在同一台宿主机中那样。它们能够在本地访问其它用户容器的端口。暴露给主机网卡的端口是通过普通Docker方式实现的。所有pod中的容器能够通过他们“10”网段(10.x.x.x)的IP地址进行通信。

除了能够避免上述动态端口分配带来的问题,该方案还能使应用平滑地从非容器环境(物理机或虚拟机)迁移到同一个pod内的容器环境。在同一台宿主机上运行应用程序栈这种场景已经找到避免端口冲突的方法(例如:通过配置环境变量)并使客户端能够找到这些端口。

该方案确实降低了pod中容器之间的隔离性——尽管端口可能存在冲突而且也不存在pod内跨容器的私有端口,但是对于需要自己的端口范围的应用程序可以运行在不同的pod中,而对于需要进行私有通信的进程则可以运行在同一个容器内。另外,该方案假定的前提条件是:在同一个pod中的容器共享一些资源(例如:磁盘卷、处理器、内存等),因此损失部分隔离性也在可接受范围之内。此外,尽管用户能够指定不同容器归属到同一个pod,但一般情况下不能指定不同pod归属于同一台主机。

当任意一个容器调用SIOCGIFADDR(发起一个获取网卡IP地址的请求)时,它所获得的IP和与之通信的容器看到的IP是一样的——每个pod都有一个能够被其它pod识别的IP。通过无差别地对待容器和pdo内外部的IP和端口,我们创建了一个非NAT的扁平化地址空间。”ip addr show”能够像预期那样正常工作。该方案能够使所有现有的域名解析/服务发现机制:包括自注册机制和分配IP地址的应用程序在容器外能够正常运行(我们应该用etcd、Euraka(用于Acme Air)或Consul等软件测试该方案)。对pod之间的网络通信,我们应该持乐观态度。在同一个pod中的容器之间更倾向于通过内存卷(例如:tmpfs)或IPC(进程间通信)的方式进行通信。

该模型与标准Docker模型不同。在该模型中,每个容器会得到一个“172”网段(172.x.x.x)的IP地址,而且通过SIOCGIFADDR也只能看到一个“172”网段(172.x.x.x)的IP地址。如果这些容器与其它容器连接,对方容器看到的IP地址与该容器自己通过SIOCGIFADDR请求获取的IP地址不同。简单地说,你永远无法在容器中注册任何东西或服务,因为一个容器不可能通过其私有IP地址被外界访问到。

我们想到的一个解决方案是增加额外的地址层:以pod为中心的一个容器一个IP模式。每个容器只拥有一个本地IP地址,且只在pod内可见。这将使得容器化应用程序能够更加容易地从物理/虚拟机迁移到pod,但实现起来很复杂(例如:要求为每个pod创建网桥,水平分割/虚拟私有的 DNS),而且可以预见的是,由于新增了额外的地址转换层,将破坏现有的自注册和IP分配机制。

当前实现

Google计算引擎(GCE)集群配置了高级路由,使得每个虚拟机都有额外的256个可路由IP地址。这些是除了分配给虚拟机的通过NAT用于访问互联网的“主”IP之外的IP。该实现在Docker外部创建了一个叫做cbr0的网桥(为了与docker0网桥区别开),该网桥只负责对从容器内部流向外部的网络流量进行NAT转发。

目前,从“主”IP(即互联网,如果制定了正确的防火墙规则的话)发到映射端口的流量由Docker的用户模式进行代理转发。未来,端口转发应该由Kubelet或Docker通过iptables进行:Issue #15

启动Docker时附加参数:DOCKER_OPTS=”–bridge cbr0 –iptables=false”。

并用SaltStack在每个node中创建一个网桥,代码见container_bridge.py:

cbr0:
container_bridge.ensure:
– cidr: {{ grains[‘cbr-cidr’] }}

grains:
roles:
– kubernetes-pool
cbr-cidr: $MINION_IP_RANGE

在GCE中,我们让以下IP地址可路由:

gcloud compute routes add “${MINION_NAMES[$i]}” \
–project “${PROJECT}” \
–destination-range “${MINION_IP_RANGES[$i]}” \
–network “${NETWORK}” \
–next-hop-instance “${MINION_NAMES[$i]}” \
–next-hop-instance-zone “${ZONE}” &

以上代码中的MINION_IP_RANGES是以10.开头的24位网络号IP地址空间(10.x.x.x/24)。

尽管如此,GCE本身并不知道这些IP地址的任何信息。

这些IP地址不是外部可路由的,因此,那些有与外界通信需求的容器需要使用宿主机的网络。如果外部流量要转发给虚拟机,它只会被转发给虚拟机的主IP(该IP不会被分配给任何一个pod),因此我们使用docker的-p标记把暴露的端口映射到主网卡上。该方案的带来的副作用是不允许两个不同的pod暴露同一个端口(更多相关讨论见Issue #390)。

我们创建了一个容器用于管理pod的网络命名空间,该网络容器内有一个本地回环设备和一块虚拟以太网卡。所有的用户容器从该pod的网络容器那里获取他们的网络命名空间。

Docker在它的“container”网络模式下从网桥(我们为每个节点上创建了一个网桥)那里分配IP地址,具体步骤如下:

  • 使用最小镜像创建一个普通容器(从网络角度)并运行一条永远阻塞的命令。这不是一个用户定义的容器,给它一个特别的众所周知的名字;
  • 创建一个新的网络命名空间(netns)和本地回路设备;
  • 创建一对新的虚拟以太网设备并将它绑定到刚刚创建的网络命名空间;
  • 从docker的IP地址空间中自动分配一个IP;
  • 在创建用户容器时指定网络容器的名字作为它们的“net”参数。Docker会找到在网络容器中运行的命令的PID并将它绑定到该PID的网络命名空间去。

其他网络实现例子

其他用于在GCE外部实现一个pod一个IP模型的方案:

挑战和未来的工作

Docker API

目前,docker inspect并不展示容器的网络配置信息,因为它们从另一个容器提取该信息。该信息应该以某种方式暴露出来。

外部IP分配

我们希望能够从Docker外部分配IP地址(Docker issue #6743),这样我们就不必静态地分配固定大小的IP范围给每个节点,而且即使网络容器重启也能保持容器IP地址固定不变(Docker issue #2801),这样就使得pod易于迁移。目前,如果网络容器挂掉,所有的用户容器必须关闭再重启,因为网络容器重启后其网络命名空间会发生变化而且任何一个随后重启的用户容器会加入新的网络命名空间进而导致无法与对方容器通信。另外,IP地址的改变将会引发DNS缓存/生存时间问题。外部IP分配方案会简化DNS支持(见下文)。

域名解析,服务发现和负载均衡

除了利用第三方的发现机制来启用自注册外,我们还希望自动建立DDNS(Issue #146)。hostname方法,$HOSTNAME等应该返回一个pod的名字(Issue #298),gethostbyname方法应该要能够解析其他pod的名字。我们可能会建立一个DNS解析服务器来做这些事情(Docker issue #2267),所以我们不必动态更新/etc/hosts文件。

服务端点目前是通过环境变量获取的。Docker链接兼容变量和kubernetes指定变量({NAME}_SERVICE_HOST和{NAME}_SERVICE_BAR)都是支持的,并会被解析成由服务代理打开的端口。事实上,我们并不使用docker特使模式来链接容器,因为我们不需要应用程序在配置阶段识别所有客户端。虽然如今的服务都是由服务代理管理的,但是这是一个应用程序不应该依赖的实现细节,客户端应该使用服务入口IP(以上环境变量会被解析成该IP)。然而,一个扁平化的服务命名空间无法伸缩,而且环境变量不允许动态更新,这使得通过应用隐性的顺序限制进行服务部署变得复杂。我们打算在DNS中为每个服务注册入口IP,并且希望这种方式能够成为首选解决方案。

我们也希望适应其它的负载均衡方案(例如:HAProxy),非负载均衡服务(Issue #260)和其它类型的分组(例如:线程池等)。提供监测应用到pod地址的标记选择器的能力使有效地监控用户组成员状态变得可能,这些监控数据将直接被服务发现机制消费或同步。用于监听/取消监听事件的事件钩(Issue #140)将使上述目标更容易实现。

外部可路由性

我们希望跨节点的容器间网络通信使用pod IP地址。假设节点A的容器IP地址空间是10.244.1.0/24,节点B的容器的IP地址空间是10.244.2.0/24,且容器A1的的IP地址是10.244.1.1,容器B1的IP地址是10.244.2.1。现在,我们希望容器A1能够直接与容器B1通信而不通过NAT,那么容器B1看到IP包的源IP应该是10.244.1.1而不是节点A的主宿主机IP。这意味着我们希望关闭用于容器之间(以及容器和虚拟机之间)通信的NAT。

我们也希望从外部互联网能够直接路由到pod。然而,我们还无法提供额外的用于直接访问互联网的容器IP。因此,我们不将外部IP映射到容器IP。我们通过让终点不是内部网络(!10.0.0.0/8)的流量经过主宿主机IP走NAT来解决这个问题,这样与因特网通信时就能通过GCE网络实现1:1NAT转换。类似地,从因特网流向内部网络的流量也能够经宿主机IP实现NAT转换/代理。

因此,让我们用三个用例场景结束上面的讨论:

  1. 容器->容器或容器<->虚拟机。该场景需要直接使用以10.开头的地址(10.x.x.x),并且不需要用到NAT;
  2. 容器->因特网。容器IP需要映射到主宿主机IP好让GCE知道如何向外发送这些流量。这里就有两层NAT:容器IP->内部宿主机IP->外部主机IP。第一层结合IP表发生在客户容器上,第二层则是GCE网络的一部分。第一层(容器IP->内部宿主机IP)进行了动态端口分配,而第二层的端口映射是1:1的;
  3. 因特网->容器。该场景的流量必须通过主宿主机IP而且理想状态下也拥有两层NAT。但是,目前的流量路径是:外部主机IP -> 内部主机IP -> Docker) -> (Docker -> 容器IP),即Docker是中间代理。一旦(issue #15)被解决,那么就应该是:外部主机IP -> 内部主机IP -> 容器IP。但是,为了实现后者,就必须为每个管理的端口建立端口转发的防火墙(iptables)。

如果我们有办法让外部IP路由到集群中的pod,我们就可以采用为每个pod创建一个新的宿主机网卡别名的方案。该方案能够消除使用宿主机IP地址带来的调度限制。

IPv6

IPv6将会是一个有意思的选项,但我们还没法使用它。支持IPv6已经被提到Docker的日程上了:Docker issue #2974Docker issue #6923Docker issue #6975。另外,主流云服务提供商(例如:AWS EC2,GCE)目前也不支持直接给虚拟机绑定IPv6地址。尽管如此,我们将很高兴接受在物理机上运行Kubernetes的用户提交的代码。

本文转自中文社区-Kubernetes设计文档 网络介绍

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
人工智能 弹性计算 运维
ACK Edge与IDC:高效容器网络通信新突破
本文介绍如何基于ACK Edge以及高效的容器网络插件管理IDC进行容器化。
|
5月前
|
Kubernetes 负载均衡 网络安全
Kubernetes 网络模型与实践
【8月更文第29天】Kubernetes(K8s)是当今容器编排领域的佼佼者,它提供了一种高效的方式来管理容器化应用的部署、扩展和运行。Kubernetes 的网络模型是其成功的关键因素之一,它支持服务发现、负载均衡和集群内外通信等功能。本文将深入探讨 Kubernetes 的网络模型,并通过实际代码示例来展示服务发现和服务网格的基本概念及其实现。
209 1
|
21天前
|
Kubernetes 网络协议 应用服务中间件
Kubernetes Ingress:灵活的集群外部网络访问的利器
《Kubernetes Ingress:集群外部访问的利器-打造灵活的集群网络》介绍了如何通过Ingress实现Kubernetes集群的外部访问。前提条件是已拥有Kubernetes集群并安装了kubectl工具。文章详细讲解了Ingress的基本组成(Ingress Controller和资源对象),选择合适的版本,以及具体的安装步骤,如下载配置文件、部署Nginx Ingress Controller等。此外,还提供了常见问题的解决方案,例如镜像下载失败的应对措施。最后,通过部署示例应用展示了Ingress的实际使用方法。
45 2
|
2月前
|
存储 人工智能
从零到一打造知识网络:你选对文档软件了吗?
在信息爆炸时代,学术工作者面临高效管理知识的挑战。传统文档管理软件难以满足“知识动态积累”与“多领域交叉”的需求。动态结构化看板通过多维度卡片化管理,以标签、层级和网状关联方式动态呈现文档和笔记,实现知识的系统化整合。例如多维标签系统和文档流动视图,能够帮助快速检索和关联资料,成为学术研究的“第二大脑”。
|
3月前
|
Kubernetes 网络协议 网络安全
k8s中网络连接问题
【10月更文挑战第3天】
231 7
|
3月前
|
Kubernetes 应用服务中间件 nginx
搭建Kubernetes v1.31.1服务器集群,采用Calico网络技术
在阿里云服务器上部署k8s集群,一、3台k8s服务器,1个Master节点,2个工作节点,采用Calico网络技术。二、部署nginx服务到k8s集群,并验证nginx服务运行状态。
1143 1
|
4月前
|
Kubernetes 容器 Perl
Kubernetes网络插件体系及flannel基础
文章主要介绍了Kubernetes网络插件体系,特别是flannel网络模型的工作原理、配置和测试方法。
134 3
|
3月前
|
Kubernetes 容器
基于Ubuntu-22.04安装K8s-v1.28.2实验(三)数据卷挂载NFS(网络文件系统)
基于Ubuntu-22.04安装K8s-v1.28.2实验(三)数据卷挂载NFS(网络文件系统)
222 0
|
4月前
|
编解码 Kubernetes 容器
有奖评测!容器服务 Kubernetes 版 ACK 文档体验评测等你来
诚邀容器服务 Kubernetes 版 ACK 用户参与文档体验评测!2024年8月14日至9月25日,完成15个场景任务并提供真实评分、改进建议及体验视频,即可获300元现金奖励;最佳建议另有100元奖金。任务需对比多云文档并按指引录制视频。详情请见活动页面与钉群通知。名额有限,速来参加!
|
5月前
|
Kubernetes Cloud Native 网络安全
云原生入门指南:Kubernetes和容器化技术云计算与网络安全:技术融合的新篇章
【8月更文挑战第30天】在云计算的浪潮中,云原生技术如Kubernetes已成为现代软件部署的核心。本文将引导读者理解云原生的基本概念,探索Kubernetes如何管理容器化应用,并展示如何通过实践加深理解。