0%

创建Tag保留规则

一个repository可以快速积累大量镜像tag,在给定时间之后或一旦它们被后续镜像构建取代后,可能不需要许多镜像tag。这些多余的tag显然会消耗大量的存储容量。作为Harbor系统管理员,可以定义规则来管理给定repository中要保留多少个tag,或将某些tag保留多长时间。

Tag保留规则如何工作

在repositories上而不是projects上定义tag保留规则(repository属于project内的概念)。在定义保留规则时,这可以提供更大的粒度。顾名思义,当我们为repositories定义保留规则时,也即在定义要保留的tag。我们没有定义规则来显式删除tag,而是当设置规则时,repositories中任何未标识为可保留的标记都将被丢弃。

tag保留规则具有3个按顺序应用的过滤器,如下表所述。

Order Filter Description
First 一个或多个repository 标识要在其上应用规则的一个或多个repository。可以标识具有特定名称或名称片段的repository,或者不具有该名称或名称片段的repository。允许使用通配符(例如* repo,repo *和**)。首先使用repository filter以标记要对其应用保留规则的repository。根据标签标准,将识别出的repository指定用于进一步匹配。在此阶段,对没有指定的repository不采取任何措施。
Second 保留数量 通过指定最大数量的标签或指定最大保留标签的时间来设置要保留的标签。
Third 要保留的标签 标识要应用规则的一个或多个标签。可以标识具有特定名称或名称片段的标签,或者不具有该名称或名称片段的标签。允许使用通配符(例如* tag,tag *和**)。

有关如何应用**通配符的信息,请参见https://github.com/bmatcuk/doublestar#patterns。

Example 1

  • 在一个project中有5个repositories,repositories A到E。
    • repository A具有100个镜像tag,所有这些镜像tag均已在上周有pull操作。
    • repository B到E每个都有6个镜像,上个月都没有pull操作。
  • 将repositories过滤器设置为**,这意味着包括了project中的所有repositories。
  • 设置保留策略,以在每个repositories中保留最近提取的10个映像。
  • 将标签过滤器设置为**,这意味着包括repositories中的所有标签。

在此示例中,规则在repository A中保留了10个最近有pull操作的镜像,并且在repository B至E中的每一个中都保留了所有6个镜像。因此,project中总共保留了34个镜像tag。换句话说,该规则不会将repository A到E中的所有镜像都视为单个池,然后从中选择10个最新镜像。因此,即使repository A中的第11至第100个标签比repository B至E中的任何标签相比都有pull操作,也将保留repository B至E中的所有标签,因为每个repository中的标签少于10个。

Example 2

本示例使用与示例1相同的project和repositories,但设置了保留策略以保留每个repository中最近7天内有pull操作的镜像。

在这种情况下,保留了repository A中的所有镜像,因为它们是最近7天内有pull操作的。repository B到E中的任何镜像都不会保留,因为它们在上周都没有发生pull操作。在此示例中,保留了100个镜像,而示例1中则保留了34个镜像。

Tag保留规则和原生Docker Tag删除问题

警告:由于本机Docker tag删除行为,当前的保留策略实施存在问题。如果有多个tag引用相同的SHA摘要,并且如果这些tag的子集被配置的保留策略标记为要删除,则所有其余tag也将被删除。这违反了保留策略,因此在这种情况下,所有tag都将保留。在以后的更新版本中将解决此问题,以便tag保留策略可以删除tag而不删除摘要和其他共享tag。

例如,我们有以下tag,这些tag根据其推送时间列出,并且它们都引用相同的SHA摘要:

  • harbor-1.8, pushed 8/14/2019 12:00am
  • harbor-release, pushed 8/14/2019 03:00am
  • harbor-nightly, pushed 8/14/2019 06:00am
  • harbor-latest, pushed 8/14/2019 09:00am

如果配置了保留策略,以保留与Harbor- *匹配的两个最新标记,以便删除Harbor-rc和Harbor-latest。但是,由于所有tag都引用相同的SHA摘要,因此此策略还将删除标签Harbor-1.8和Harbor-release,因此将保留所有标签。(时间和删除的tag有点对不上)

在一个Repository上合并规则

每个project最多可以定义15条规则。我们可以将多个规则应用于一个repository或一组repositories。在将多个规则应用于repository 时,它们将使用OR逻辑而不是AND逻辑来应用。这样,就不会在给定的repository 上优先应用规则。规则在后台同时运行,每个规则的结果集在运行结束时合并。

Example 3

本示例使用与示例1和2相同的project和repositories,但是设置了两个规则:

  • 规则1:保留每个repository中最近7天内pull操作的所有镜像。
  • 规则2:每个repository中最多保留10个镜像。

对于repository A,规则1保留所有镜像,因为它们都是在上周发生pull操作的。规则2保留最近发生pull操作的10个镜像。因此,由于这两个规则是通过OR关系应用的,所有100个镜像都保留在repository A中。

对于repositories B-E,规则1将保留0个镜像,因为上周未发生pull操作。规则2将保留所有6个镜像,因为6 <10。因此,由于这两个规则以OR关系应用,对于repositories B-E,每个repository将保留所有6个图像。

在此示例中,所有镜像均被保留。

Example 4

本示例使用与先前示例不同的repository。

  • 包含12个tag的repository:

    Production Release Candidate Release
    2.1-your_repo-prod 2.1-your_repo-rc 2.1-your_repo-release
    2.2-your_repo-prod 2.2-your_repo-rc 2.2-your_repo-release
    3.1-your_repo-prod 3.1-your_repo-rc 3.1-your_repo-release
    4.4-your_repo-prod 4.4-your_repo-rc 4.4-your_repo-release
  • 在此repository上定义了3个tag保留规则:

    • 保留以2开头的10个最近发生pull操作的镜像tag。

    • 保留以-prod结尾的10个最近发生pull操作的镜像tag。

    • 保留所有不包含2.1-your_repo-prod的tag。

在此示例中,规则将应用于以下7个tag(与预期不符,官方收到反馈后已修改):

  • 2.1-your_repo-rc
  • 2.1-your_repo-release
  • 2.2-your_repo-prod
  • 2.2-your_repo-rc
  • 2.2-your_repo-release
  • 3.1-your_repo-prod
  • 4.4-your_repo-prod

Tag保留规则如何与项目配额搭配使用

Harbor系统管理员可以设置一个project可以包含的tag数量及其可以使用的存储量的最大值。有关project配额的信息,请参阅配置project配额

如果在project上设置配额,则不能超过该配额。即使设置的保留规则超过配额,配额也将应用于project。换句话说,不能使用保留规则来绕过配额。

配置Tag保留规则实例

  1. 使用至少具有项目管理员特权的帐户登录到Harbor界面。

  2. 转到”project”,选择一个project,然后选择tag保留。

    tag-retention

  3. 单击”添加规则”以添加规则。

  4. 在“repositories”下拉菜单中,选择匹配或排除。

    tag-retention

  5. 在“repositories”文本框中,标识要在其上应用规则的repository。

    可以通过输入以下信息来定义要在哪些repositories上应用规则:

  • repository名称,例如my_repo_1。

  • 以逗号分隔的repository名称列表,例如my_repo_1,my_repo_2,your_repo_3。

  • 带通配符的部分repository名称,例如my _ _ 3或*repo*。

  • **将规则应用于project中的所有repositories。

    如果选择匹配,则规则将应用于标识的repository。如果选择排除,则该规则将应用于project中除已标识的repositories之外的所有repositories。

  1. 在“按镜像或天数计数”下拉菜单中,定义要保留的tag数量或保留的时间。

    tag-retention

Option Description
retain the most recently pushed # images 输入要保留的最大镜像个数,保留最近发生push操作的镜像。不考虑镜像的时间。
retain the most recently pulled # images 输入要保留的最大镜像个数,保留最近发生pull操作的镜像。不考虑镜像的时间。
retain the images pushed within the last # days 输入保留镜像的天数,仅保留在此期间发生push操作的镜像。不考虑镜像数量。
retain the images pulled within the last # days 输入保留镜像的天数,仅保留在此期间发生pull操作的镜像。不考虑镜像数量。
retain always 始终保留此规则标识的镜像。
  1. 在”tag”下拉菜单中,选择匹配或排除。

  2. 在“标签”文本框中,标识要在其上应用规则的tag。

    可以通过输入以下信息来定义要在其上应用规则的tag:

  • tag名称,例如my_tag_1。
  • tag名称的逗号分隔列表,例如my_tag_1,my_tag_2,your_tag_3。
  • 带通配符的部分tag名称,例如my _ _ 3或* tag *。
  • **将规则应用于project中的所有tag。
  1. 单击”添加”以保存规则。

  2. (可选)单击“添加规则”以添加更多规则,每个project最多15条。

  3. (可选)在“计划”下,单击“编辑”,然后选择运行规则的频率。

    tag-retention

    如果选择“自定义”,请输入cron job命令以配置规则。

    注意:如果定义多个规则,则计划将应用于所有规则。不能配置不同的规则在不同的时间运行。

  4. 单击“模拟运行”以测试定义的一个或多个规则。

  5. 单击“立即运行”以立即运行规则。

警告:运行规则后,将无法还原它。强烈建议先执行模拟运行,然后再运行规则。

要修改现有规则,请使用规则旁边的操作下拉菜单来禁用,编辑或删除该规则。

tag-retention

Big picture

了解Calico支持的各种网络选项,以便可以根据需要选择最佳选项。

Value

Calico灵活的模块化体系结构支持广泛的部署选项,因此可以根据自己的特定环境和需求选择最佳的网络方案。这包括在BGP和非BGP的情况下,以underlying或overlay模式与各种CNI和IPAM插件以及基础网络类型一起运行的能力。

Concepts

如果想完全了解可用的网络选项,我们建议确保自己熟悉并理解以下概念。如果希望跳过学习并直接获得选择和建议,则可以跳至“网络选项”。

Kubernetes网络基础知识

Kubernetes网络模型定义了一个“扁平”网络,其中:

  • 每个Pod都有自己的IP地址。
  • 无需NAT,任何节点上的Pod均可与所有其他节点上的所有Pod通信。

这将创建一个干净的,向后兼容的模型,从端口分配,命名,服务发现,负载平衡,应用程序配置和迁移的角度来看,可以将Pod像VM或物理主机一样对待。可以使用网络策略来定义网络分段,以将流量限制在这些基本网络功能内。

在此模型中,可以灵活地支持不同的网络方案和环境。确切地如何实现网络的详细信息取决于所使用的CNI,网络和云提供商插件的组合。

CNI插件

CNI(容器网络接口)是一个标准API,允许将不同的网络实现插入Kubernetes。每当创建或销毁Pod时,Kubernetes都会调用API。CNI插件有两种类型:

  • CNI网络插件:负责向Kubernetes Pod网络中添加Pod或从Kubernetes Pod网络中删除Pod。这包括创建/删除每个Pod的网络接口,以及将其连接/断开与其他网络实现的连接。
  • CNI IPAM插件:负责在Pod创建或删除时分配和释放Pod的IP地址。根据插件的不同,这可能包括为每个节点分配一个或多个IP地址(CIDR)范围,或从底层公共云网络获取IP地址以分配给Pod。

云提供商集成

Kubernetes云提供商集成是特定于云的控制器,可以配置基础云网络以帮助提供Kuberenetes网络。根据云提供商的不同,这可能包括自动将路由编程到基础云网络中,以便它本机知道如何路由Pod流量。

Kubenet

Kubenet是Kubernetes中内置的一个非常基本的网络插件。它没有实现跨节点通信或网络策略。它通常与云提供商集成一起使用,后者在云提供商网络中设置路由以在节点之间或在单节点环境中进行通信。Kubenet与Calico不兼容。

Overlay网络

overlay网络是位于另一个网络之上的网络。在Kubernetes的上下文中,overlay网络可用于处理基础网络顶部节点之间的Pod到Pod流量,这些节点不知道Pod IP地址或哪些Pod在哪些节点上运行。overlay网络通过将基础网络不知道如何处理(例如使用Pod IP地址)的网络数据包封装在基础网络确实知道如何处理的外部数据包(例如节点IP地址)中。用于封装的两种常见网络协议是VXLAN和IP-in-IP。

使用overlay网络的主要优点是它减少了对基础网络的依赖性。例如,可以在几乎任何基础网络之上运行VXLAN,而无需与基础网络集成或对其进行任何更改。

使用overlay网络的主要缺点是:

  • 对性能有轻微影响。封装数据包的过程占用少量CPU,并且数据包中用于编码封装(VXLAN或IP-in-IP标头)所需的额外字节减少了可以发送的内部数据包的最大大小,这意味着需要为相同数量的总数据发送更多数据包。
  • Pod IP地址无法在集群外部路由。

跨子网Overlay网络

除了标准的VXLAN或IP-in-IP overlay外,Calico还支持VXLAN和IP-in-IP的“cross-subnet”模式。在这种模式下,在每个子网中,基础网络充当L2网络。在单个子网内发送的数据包不进行封装,因此可以获得非overlay网络的性能。跨子网发送的数据包像普通的overlay网络一样被封装,从而减少了对基础网络的依赖(无需与基础网络集成或对其进行任何更改)。

就像使用标准overlay网络一样,基础网络也不知道Pod IP地址,并且Pod IP地址无法在集群外部路由。

Pod IP路由到集群外部的能力

不同的Kubernetes网络实现的一个重要区别特征是Pod IP地址是否可在整个较宽网络的集群外部路由。

不可路由

如果Pod IP地址无法在集群外部路由,则当Pod尝试建立与集群外部IP地址的网络连接时,Kubernetes将使用一种称为SNAT(源网络地址转换)的技术来更改源IP从Pod的IP地址到托管Pod的节点的IP地址。连接上的所有返回数据包都会自动映射回Pod IP地址。因此,Pod不知道发生了SNAT,连接的目的地将节点视为连接的源,而底层的更广泛的网络不会看到Pod IP地址。

对于相反方向的连接,其中集群外部的某些设备需要连接到Pod,这只能通过Kubernetes service或Kubernetes ingress来完成。集群之外的任何人都无法直接连接到Pod IP地址,因为更广泛的网络不知道如何将数据包路由到Pod IP地址。

可路由

如果Pod IP地址可以在集群外部路由,则Pod可以不使用SNAT即可连接到外部世界,并且集群外部可以直接连接到Pod,而无需通过Kubernetes service或Kubernetes ingress。

Pod IP可路由到集群外部的优点是:

  • 避免将SNAT用于出站连接对于与现有更广泛的安全要求进行集成可能至关重要。它还可以简化操作日志的调试和易懂性。
  • 如果有专门的工作负载,这意味着某些Pod需要直接访问而不需要通过Kubernetes service或Kubernetes ingress,那么可路由的Pod IP在操作上可能更简单。

Pod IP地址可路由到集群外的主要缺点是,Pod IP在整个网络中必须是唯一的。因此,例如,如果运行多个群集,则需要为每个群集中的Pod使用不同的IP地址范围(CIDR)。当大规模运行时,或者如果现有其他企业对IP地址空间有大量重要需求,这又可能导致IP地址范围耗尽的挑战。

决定可路由性的因素是什么?

如果集群使用overlay网络,则Pod IP通常无法路由到集群外。

如果集群不使用overlay网络,那么Pod IP是否路由到集群外取决于所用的CNI插件,云提供商集成或与物理网络(对于本地)BGP对等连接。

BGP

BGP(边界网关协议)是用于跨网络共享路由的基于标准的网络协议。它是互联网的基本组成部分之一,具有出色的扩展特性。

Calico内置了对BGP的支持。在本地部署中,这使Calico可以与物理网络(通常连接到Top或Rack路由器)建立对等关系以交换路由,从而形成一个none-overlay网络,其中Pod IP地址可以在更广泛的网络中路由,就像附加的任何其他工作负载一样到网络。

关于Calico网络

Calico网络灵活的模块化架构包括以下内容。

Calico CNI网络插件

Calico CNI网络插件使用一对虚拟以太网设备(一对)将Pod连接到主机网络名称空间的L3路由。这种L3架构避免了许多其他Kubernetes网络解决方案中附加的L2桥不必要的复杂性和性能开销。

Calico CNI IPAM插件

Calico CNI IPAM插件为一个或多个可配置IP地址范围之外的Pod分配IP地址,并根据需要为每个节点动态分配IP块。与许多其他CNI IPAM插件(包括在许多网络解决方案中使用的主机本地IPAM插件)相比,具有更有效的IP地址空间使用。

Overlay网络模式

Calico可以提供VXLAN或IP-in-IP网络,包括cross-subnet模式。

Non-overlay网络模式

Calico可以提供在任何基础L2网络之上运行的non-overlay网络,或者是具有适当的云提供商集成的公共云网络或支持BGP的L3网络(通常是具有标准Top-of-Rack路由器)。

网络策略

Calico的网络策略执行引擎实现了Kubernetes网络策略的全部功能,以及Calico Network Policy的扩展功能。这可以与Calico的内置网络模式或任何其他Calico兼容的网络插件和云提供商集成结合使用。

与Calico兼容的CNI插件和云提供商集成

除Calico CNI插件和内置网络模式外,Calico还与许多第三方CNI插件和云提供商集成兼容。

Amazon VPC CNI

Amazon VPC CNI插件从基础AWS VPC分配Pod IP,并使用AWS弹性网络接口提供VPC本机Pod网络(可在集群外部路由的Pod IP)。它是Amazon EKS中使用的默认网络,并与Calico一起用于网络策略实施。

Azure CNI

Azure CNI插件从基础Azure VNET分配Pod IP,将Azure虚拟网络配置为提供VNET本机Pod网络(可在群集外部路由的Pod IP)。它是Microsoft AKS中使用的默认网络,可与Calico一起执行网络策略。

Azure cloud provider

Azure云提供商集成可以用作Azure CNI插件的替代方法。它使用host-local IPAM插件分配Pod IP,并使用相应的路由对基础Azure VNET子网进行编程。Pod IP仅可在VNET子网内路由(这通常意味着它们无法路由到群集外部)。

Google cloud provider

Google云提供商集成使用host-local IPAM插件分配Pod IP,并对Google Cloud网络Alias IP范围进行编程,以在Google Cloud上提供VPC本机Pod网络(可在集群外部路由的Pod IP)。它是Google Kubernetes Engine(GKE)的默认设置,并带有Calico来执行网络策略。

Host local IPAM

host-local IPAM插件是常用的IP地址管理CNI插件,它为每个节点分配固定大小的IP地址范围(CIDR),然后从该范围内分配Pod IP地址。默认地址范围大小是256个IP地址(a/24),其中两个IP地址是为特殊目的保留的,未分配给Pod。host-local IPAM插件的简单性使其易于理解,但与Calico CNI IPAM插件相比,其IP地址空间使用效率较低。

Flannel

Flannel使用从host-local IPAM插件获得的静态CIDR路由pod间的通信。Flannel提供了许多网络后端,但主要与VXLAN后端一起使用。Calico CNI和Calico网络策略可以与flannel和host-local IPAM插件结合使用,以提供具有策略实施功能的VXLAN网络。这种组合有时称为“Canal”。

注意:Calico现在内置了对VXLAN的支持,为了简化起见,我们通常建议优先使用Calico + Flannel组合。

网络选择

本地

Calico本地部署最常见的网络设置是non-overlay模式,该模式使用BGP与物理网络(通常是机架路由器的顶部)对等,以使Pod IP可在集群外部路由。(当然,可以根据需要配置其余的本地部署网络,以限制群集外的Pod IP路由的范围。)此设置提供了丰富的Calico高级功能,包括公告Kubernetes serviceIP的能力(cluster IPs or external IPs),以及在Pod,名称空间或节点级别控制IP地址管理的能力,以支持与现有企业网络和安全要求集成的各种可能性。

Policy IPAM CNI Overlay Routing
Calico Calico Calico No BGP

如果不能将BGP对等连接到物理网络,并且群集在单个L2网络中,则还可以运行non-overlay模式,而Calico只能在群集中的节点之间对等BGP。即使这不是严格的overlay网络,也无法在集群外部路由Pod IP,因为基础网络没有Pod IP的路由。

Policy IPAM CNI Overlay Routing
Calico Calico Calico No BGP

或者,可以在VXLAN或IP-in-IP模式下运行Calico,并使用cross-subnet模式来优化每个L2子网内的性能。

推荐方案:

Policy IPAM CNI Overlay Routing
Calico Calico Calico VXLAN Calico

替代方案:

Policy IPAM CNI Overlay Routing
Calico Calico Calico IPIP BGP

AWS

如果希望在集群外部可路由Pod IP地址,则必须使用Amazon VPC CNI插件。这是EKS的默认网络模式,并使用Calico的网络策略。Pod IP地址是从基础VPC分配的,每个节点的Pod的最大数量取决于实例类型。

Policy IPAM CNI Overlay Routing
Calico AWS AWS No VPC/Native

如果希望避免依赖特定的云提供商,或者由于IP地址范围耗尽的挑战,或者如果Amazon VPC CNI插件每个节点支持的最大Pod数量不足以从基础VPC分配Pod IP,则存在问题。根据需求,我们建议使用Calico的overlay + cross-subnet模式。Pod IP将无法在集群外部路由,但是可以在不依赖基础云网络的情况下将集群扩展到Kubernetes的极限。

Policy IPAM CNI Overlay Routing
Calico Calico Calico VXLAN Calico

在这个简短的视频中,可以了解有关AWS上的Kubernetes Networking的更多信息,包括上述每个选项的工作原理。Everything you need to know about Kubernetes networking on AWS

Azure

如果希望在群集外部可以路由Pod IP地址,则必须使用Azure CNI插件。这由AKS和Calico进行网络策略支持。Pod IP地址是从基础VNET分配的。

Policy IPAM CNI Overlay Routing
Calico Azure Azure No VPC/Native

如果要使用AKS,但由于IP地址范围耗尽的问题而无法从基础VNET分配Pod IP,则可以将Calico与Azure云提供商集成一起使用。它使用host-local IPAM为每个节点分配/24地址段,并为这些/24地址段在群集的基础VNET子网中编程路由。在群集/VNET子网外部无法路由Pod IP,因此如果需要,可以在多个群集中使用相同的Pod IP地址范围(CIDR)。

注意:在某些AKS文档中将其称为kubenet + Calico,但实际上是带有Azure云提供程序的Calico CNI,并且不使用kubenet插件。

Policy IPAM CNI Overlay Routing
Calico Host Local Calico No VPC/Native

如果不使用AKS,而是希望避免依赖于特定的云提供商,或者由于IP地址范围耗尽的问题而无法从基础VNET分配Pod IP,那么我们建议使用Calico的overlay + cross-subnet模式。Pod IP将无法在集群外部路由,但是可以在不依赖基础云网络的情况下将集群扩展到Kubernetes的极限。

Policy IPAM CNI Overlay Routing
Calico Calico Calico VXLAN Calico

可以在此短视频中了解有关Azure上Kubernetes Networking的更多信息,包括上述每个选项的工作原理:Everything you need to know about Kubernetes networking on Azure

Google Cloud

如果想在集群外部路由Pod IP地址,则必须将Google云提供商集成与host-local IPAM CNI插件结合使用。GKE和Calico都为网络策略提供了支持。从基础VPC分配Pod IP地址,并自动将相应的Alias IP地址分配给节点。

Policy IPAM CNI Overlay Routing
Calico Host Local Calico No VPC/Native

如果希望避免依赖特定的云提供商,或者由于IP地址范围耗尽的挑战而无法从基础VPC分配Pod IP,那么我们建议使用Calico的overlay模式。由于Google云网络是纯L3网络,因此不支持cross-subnet模式。Pod IP将无法在集群外部路由,但是可以在不依赖基础云网络的情况下将集群扩展到Kubernetes的极限。

推荐方案:

Policy IPAM CNI Overlay Routing
Calico Calico Calico VXLAN Calico

替代方案:

Policy IPAM CNI Overlay Routing
Calico Calico Calico IPIP BGP

可以在此短片中了解有关Google云上的Kubernetes Networking的更多信息,包括上述每个选项的工作原理:Everything you need to know about Kubernetes networking on Google cloud

IBM Cloud

如果使用的是IBM Cloud,则建议使用IKS,该产品具有内置Calico的功能,可提供cross-subnet +IPIP模式的网络模式。除了为Pod提供网络策略外,IKS还使用Calico网络策略来保护群集中的主机节点。

Policy IPAM CNI Overlay Routing
Calico Calico Calico IPIP BGP

Anywhere

上面的环境列表显然并不详尽。理解本指南中的概念和解释有助于确定适合的环境。如果仍然不确定,则可以通过Calico用户的Slack或Discourse论坛寻求建议。记住,如果要使用,而不想担心各种选项,则可以在几乎任何环境中以VXLAN + overlay模式运行Calico。

Policy IPAM CNI Overlay Routing
Calico Calico Calico VXLAN Calico

简介

边界网关协议(BGP)是互联网涉及的核心技术之一,它使网络能够相互通信。了解如何使用此工具来定义网络拓扑,不仅可以让我们更好地了解互联网络,还可以将这种鲁棒性应用到自己的网络。

在本教程结束时,我们将熟悉BGP的核心概念,并能够把相关的术语传达给其他人。此外,我们还将学会使用BIRD的用户界面建立对等会话并开始发布路由。

本文使用docker容器实现这一目标。为了完成本教程,需要确保已安装dockerdocker-compose

准备

首先,需要克隆bird_examples_docker项目。

1
2
3
ncatelli@ofet> git clone https://github.com/ncatelli/bird_examples_docker.git
ncatelli@ofet> cd bird_examples_docker
ncatelli@ofet> docker-compose up -d

上述命令将创建三个容器(peer1peer2peer3),所有这些容器均已安装BIRD并已建立对等会话。如果还不知道这意味着什么,请不要担心,我们将在设置好BGP并准备就绪后对其进行介绍。

首先连接到peer1并检查所有设置是否正确。

1
2
3
4
5
6
7
8
9
ncatelli@ofet> docker-compose exec peer1 bash
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:03
device1 Device master up 02:36:03
direct1 Direct master up 02:36:03
peer2 BGP master up 02:36:08 Established
peer3 BGP master up 02:36:07 Established

如果发现peer2peer3已经显示Established,则说明一切正常,我们已准备就绪。在开始使用之前,将简要介绍BGP的工作原理。

原理概述

边界网关协议(BGP)是一种外部网关协议,用于在自治系统之间交换路由信息。自治系统(AS)是路由前缀和策略的组织单位。这些AS由唯一的16位(后来扩展到32位)自治系统编号(ASN)标识。例如,Facebook的ASN为32934或通常显示为AS32934。BGP的强大之处在于其在成千上万个分散式AS之间传递路由协议和策略的能力。

互联网以及许多其他网络由相互之间进行通信的许多自治系统组成。对等会话促进了这种通信,该会话允许两个AS交换策略,路由和链接状态。所有这些信息都在两个BGP守护程序之间交换,这两个守护程序在TCP的179端口上进行侦听。

虽然BGP被认为是用于在Internet上的大型组织之间进行路由的外部网关协议,但它也可以在AS中使用,以使网络工程师能够控制其内部网络的拓扑。这是eBGP和iBGP术语的来源。iBGP将是本教程其余部分的重点。现在,我们将开始使用BIRD及其交互式命令行工具birdc尝试这些对等会话。

BIRD简介

BIRD是功能齐全的路由守护程序,它支持许多不同的路由协议,包括BGP。BIRD提供了一种简单的配置格式和命令行,用于与会话进行交互。BIRD还内置了对IPv4和IPv6的支持,以及与这两种协议一起使用的相应工具。

检查BGP会话

与我们验证是否正确配置了docker环境的方式类似,我们可以通过运行以下命令查看正在运行的会话:

1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master up 02:36:07 Established
peer3 BGP master up 02:36:06 Established

这给了我们很多信息。但是,让我们关注最后两个条目,peer2peer3。我们可以看到它们都是BGP协议,并且info字段显示已经Established。这些条目中的每一个都对应于peer1peer2peer3打开的BGP会话。为了演示这些值与正在运行的会话之间的关系,让我们在peer2上停止Bird服务。在新的终端窗口中,运行以下命令来停止peer2,模拟网络故障。

1
2
ncatelli@ofet> docker-compose stop peer2
Stopping bird_examples_peer2_1 ... done
1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master start 02:43:38 Connect Socket: Connection closed
peer3 BGP master up 02:36:06 Established

通过重新启动peer2,BIRD应该重新启动,并且随后应重新建立对等会话。

1
2
ncatelli@ofet> docker-compose start peer2
Starting peer2 ... done
1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master up 02:46:29 Established
peer3 BGP master up 02:36:06 Established

通过停止peer2上的bird守护程序,我们使端口179上的TCP连接在peer1peer2之间关闭。这样做会将我们的对等会话从Established更改为Connect。这两个状态对应于许多BGP状态中的两个,但是出于本教程的考虑,我们将仅关注Established,并将所有其他值视为未建立。对于那些更好奇的人,可以在Wikipedia上有关BGP的文章中找到有关会话状态的更多信息。

配置BGP会话

尽管现在知道了如何检查会话是否已经建立,但了解这些会话的配置也很重要。为此,我们需要深入研究bird配置文件。让我们看一下peer1上的/etc/bird下的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@peer1:~# cat /etc/bird/bird.conf
router id 10.0.0.10;

protocol kernel {
metric 0;
import none;
learn;
export all;
}

protocol device {
}

protocol direct {
}

protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import all;
export all;
}

protocol bgp peer3 {
local as 64512;
neighbor 10.0.100.11 as 64514;
import all;
export all;

我们可以看到建立这些初始会话所需的配置非常少。为了更深入地了解这项工作的真正作用,我们将专注于一个特定块:bgp peer2

1
2
3
4
5
6
protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import all;
export none;
}

在本教程的前面,我们讨论了eBGP和iBGP之间的区别以及大型AS如何使用唯一的ASN标识自己。但是,一小部分可用的ASN已保留给专用iBGP使用。这个范围是64512-65534。知道了这一点,我们可以看到我们已经将私有范围中的ASN分配给了peer2peer1被分配了ASN 64512。

查看下一条语句,我们可以看到具有IP和附加AS的邻居语句。该IP对应于我们尝试与之建立会话的主机或BGP术语中的邻居,而64513对应于我们分配给peer2主机的AS。可以通过查看peer2上的配置文件来确认这一点。

1
2
3
4
5
6
root@peer2:/# grep -A4 peer1 /etc/bird/bird.conf
protocol bgp peer1 {
local as 64513;
neighbor 10.0.0.10 as 64512;
export none;
}

协议BGP块中的这两个指令处理会话的初始建立。

虽然建立和维护会话对于BGP的运行至关重要,但仅建立会话无法路由任何流量。在下一节中,我们将探讨配置文件中的其他一些元素,以及如何使用它们来发现和宣布节点之间的路由。在继续进行此操作之前,先回顾一下我们当前的拓扑。

当前,我们的网络中有三个节点,peer1(AS64512),peer2(AS64513)和peer3(AS64514)。这些配置在同一广播域中,但是对等的结构类似于peer3 <-> peer1 <-> peer2。这种结构允许通过我们的路由服务器peer1peer2peer3进行路由通信。在继续进行本教程的下一步,即发布路由时,请牢记此拓扑。

IP地址 节点名 AS号
10.0.0.10 peer1 64512
10.0.0.11 peer2 64513
10.0.100.11 peer3 64514

BGP发布路由

内核协议

在开始发布Bird守护程序之间的路由之前,我们应该首先了解BIRD如何在Linux内核和BIRD守护程序之间传递路由。这就是我们前面看到的内核协议块起作用的地方。

1
2
3
4
5
6
protocol kernel {
metric 0;
learn;
import none;
export all;
}

kernel块中可以指定许多选项,并且可以在此处找到关于这些选项的更多信息,但是我们要执行的大部分操作由导入/导出定义。

1
import none;

告诉BIRD不要将路由从内核路由表中读取到BIRD中。我们将通过直接协议(即配置)获取路由。

1
export all;

告诉BIRD将其他公告了解的所有路由导出到内核的路由表中。这使我们可以实际利用此主机上的任何已获取的路由。

1
metric 0;

度量标准值由内核用来确定路由的优先级,并选择优先级最低的路由。在这种情况下,我们将其设置为0或未定义,以便我们首选本地路由。

1
learn;

最后,我们将设置学习指令,该指令将允许其他守护程序从内核路由表中学习路由。

发现直接路由

现在,我们已经配置了BIRD守护程序以将路由直接推送到内核路由表,我们将需要配置对等端以发现本地直接路由。由于我们会将这些路由直接添加到我们的环回接口,因此在选择的编辑器中,将直接协议配置为仅使用lo接口。

1
2
3
4
5
6
ncatelli@ofet> grep -A2 direct conf/peer2/etc/bird/bird.conf
protocol direct {
interface "lo";
}
ncatelli@ofet> docker-compose restart peer2
Restarting bird_examples_peer2_1 ... done

由于我们的网络上也有peer3,因此在此主机上进行相同操作,以防止宣布其他任何路由。

1
2
3
4
5
6
ncatelli@ofet> grep -A2 direct conf/peer3/etc/bird/bird.conf
protocol direct {
interface "lo";
}
ncatelli@ofet> docker-compose restart peer3
Restarting bird_examples_peer3_1 ... done

此时,除了默认的10.0.0.0子网(可以使用birdc进行验证)以外,我们将没有其他路由学习和发布。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@peer2:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 via 10.0.0.10 on eth0 [peer1 03:05:02] ! (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.0.10
BGP.local_pref: 100
10.0.100.0/24 via 10.0.0.10 on eth0 [peer1 03:05:02] * (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.0.10
BGP.local_pref: 100

过滤引用和导出

与内核模块类似,导出和导入可用于控制BGP对等方导入和导出的内容。首先,我们探讨过滤的概念以及如何将其用于控制将宣布或导出哪些路由。

BIRD中的过滤器基本上是在路由上执行的函数,返回接受或拒绝。这使我们能够应用一种简单的编程语言来为我们的路由策略添加逻辑。过滤器可以包含任何内容,从单个语句到非常复杂的逻辑。首先,让我们将none和all指令重新实现为过滤器,然后将它们添加到include指令上方的bird.conf文件中。

1
2
3
filter accept_all {
accept;
};
1
2
3
filter reject_all {
reject;
};

现在我们已经有了过滤器,让我们在我们的协议块之一的导入/导出指令中实现它们。在主机peer1上,让我们看一下peer2块。

1
2
3
4
5
6
protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import filter accept_all;
export filter accept_all;
}

从功能上讲,这与我们的原始配置相同,但是现在我们可以使用进一步的逻辑扩展这些设置。通过进一步研究过滤器脚本语言,可以了解这些过滤器的功能。为了扩展我们所学的知识,让我们在peer2上的bird.conf中创建一个过滤器,以控制要向peer1发布的路由。

1
2
3
4
5
6
filter export_subnets {
if net ~ [ 192.168.5.5/32 ] then {
accept;
}
reject;
}

最后,我们需要在peer2上更新peer1以使用此导出过滤器。

1
2
3
4
5
6
root@peer2:/# grep -A4 peer1 /etc/bird/bird.conf
protocol bgp peer1 {
local as 64513;
neighbor 10.0.0.10 as 64512;
export filter export_subnets;
}
1
2
3
ncatelli@ofet> docker-compose restart peer1 peer2
Restarting bird_examples_peer2_1 ... done
Restarting bird_examples_peer1_1 ... done

发布路由

现在,我们有了开始发布peer1peer2之间的路由所需的所有构造块。在此之前,让我们回顾一下我们所做的事情。首先,我们已将BIRD守护程序配置为使用我们的内核协议在其内部路由表和内核路由表之间进行通信。我们已将BIRD守护程序配置为从具有直接协议的环回接口中学习路由。我们还配置了peer1从其他对等节点导入路由并导出这些路由。最终,我们将peer2配置为仅使用export_subnets过滤器将192.168.5.5/32导出到peer1。但是,目前我们还没有从peer2通告到peer1的路由。

1
2
3
4
root@peer1:/# ip route  
default via 10.0.0.1 dev eth0
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.10
10.0.100.0/24 dev eth1 proto kernel scope link src 10.0.100.10

此时,所有设置已经完成,因此可以从环回接口中学习路由。通过将IP添加到peer2的环回接口上,我们应该能够看到路由的发布。

1
root@peer2:/# ip a add 192.168.5.5/32 dev lo

现在,如果我们同时查看peer1上的birdc和内核路由表,我们应该可以看到peer1上新IP的路由。

1
2
3
4
5
6
root@peer1:~# ip route
default viia 10.0.2.2 dev eth0
10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.10
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
10.0.100.0/24 dev eth2 proto kernel scope link src 10.0.100.10
192.168.5.5 via 10.0.0.11 dev eth1 proto bird
1
2
3
4
5
6
7
8
9
10
11
12
root@peer1:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 dev eth0 [direct1 03:10:33] * (240)
Type: device unicast univ
10.0.100.0/24 dev eth1 [direct1 03:10:33] * (240)
Type: device unicast univ
192.168.5.5/32 via 10.0.0.11 on eth0 [peer2 03:12:39] * (100) [AS64513i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64513
BGP.next_hop: 10.0.0.11
BGP.local_pref: 100

ping将显示我们现在可以从peer1向该主机发送流量。

1
2
3
4
5
6
7
root@peer1:/# ping -c 1 192.168.5.5
PING 192.168.5.5 (192.168.5.5) 56(84) bytes of data.
64 bytes from 192.168.5.5: icmp_seq=1 ttl=64 time=0.135 ms

--- 192.168.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.135/0.135/0.135/0.000 ms

现在可以看到这些路由已通过peer1通告到peer3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@peer3:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 via 10.0.100.10 on eth0 [peer3 03:10:37] * (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
10.0.100.0/24 via 10.0.100.10 on eth0 [peer3 03:10:37] ! (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
192.168.5.5/32 via 10.0.100.10 on eth0 [peer3 03:12:38] * (100) [AS64513i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512 64513
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
1
2
3
4
5
6
7
root@peer3:/# ping -c 1 192.168.5.5
PING 192.168.5.5 (192.168.5.5) 56(84) bytes of data.
64 bytes from 192.168.5.5: icmp_seq=1 ttl=63 time=0.082 ms

--- 192.168.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.082/0.082/0.082/0.000 ms
1
2
3
4
root@peer3:/# traceroute 192.168.5.5
traceroute to 192.168.5.5 (192.168.5.5), 64 hops max
1 10.0.100.10 0.005ms 0.003ms 0.003ms
2 192.168.5.5 0.003ms 0.003ms 0.003ms

我们也可以通过查看AS PATH来了解情况。通过查看与birdc中的路由关联的AS PATH,可以看到从64513到64512的路由在到达peer3之前发布。

因为将peer1配置为将路由导出到peer3,并且由于peer3配置为从peer1导入路由,所以我们能够将此路由获取到peer3的BIRD路由表中。然后,由于我们已将内核协议配置为在BIRD中导出路由,因此这些路由会将其放入peer3的内核路由表中。

下一步

在这个简单的教程中,我们探讨了许多概念,但是,我们几乎没有涉及bird的概念。随时使用此示例,以发布和过滤路由的方式进一步探索。在以后的教程中,我们将更深入地探讨BGP的工作方式以及用于确定路由的过程,包括如何通信和本地优先级以及BGP守护程序如何使用它们来选择通往服务器的最佳路径。我们还将探讨什么是任播IP,以及如何使用BGP配置高可用性,以及如何使用过滤策略代替直接接口策略来控制向每个节点声明哪些前缀。BGP可以为我们提供对网络拓扑的大量控制,并且了解如何使用BGP可以更好地调整网络以适应自己的需求。

在构建裸机Kubernetes集群时,您可能会遇到一个常见问题,就像我在做的那样,除了使用NodePort之外,您真的不知道如何向Internet公开Kubernetes服务。如果使用的是NodePort服务类型,它将分配一个要打开的较大端口号,并且您必须允许防火墙规则连接到这些端口。这对您的基础架构不利,尤其是在将服务器暴露于外部Internet时。好吧,还有另一种简洁的方法可以将您的Kubernetes服务公开出去,并且使用服务的原始端口号。例如,您可以将Kubernetes群集中部署的MySQL服务通过3306而不是32767端口暴露给外界。答案是使用Kubernetes External IP服务类型

就个人而言,我发现在Kubernetes社区中并未广泛讨论此主题,这可能是因为许多人正在使用云提供商的负载均衡器或将Metal LB用于本地部署。

什么是External IP服务

Kubernetes官方文档中可以看到External IP的描述:

如果存在路由到一个或多个集群节点的外部IP,则Kubernetes Services可以在这些External IP上公开。在服务端口上使用外部IP(作为目标IP)进入群集的流量将被路由到服务端点之一。External IP不受Kubernetes的管理,由集群管理员负责。

对于大多数人来说,这种解释是可以理解的。这里最重要的是确保使用哪个IP来访问Kubernetes集群。使用External IP服务类型,我们可以将服务绑定到用于连接集群的IP。

如果您了解Kubernetes网络的工作方式,那将是很好的。如果您不熟悉它,请查看Mark Betz撰写的博客文章,以详细了解它们。这里最重要的是要知道Kubernetes网络与Overlay网络一起工作。这意味着一旦您到达群集中的任何节点(主节点或工作节点),您就可以虚拟访问群集中的所有节点。

下图就是他们的组网图:

kubernetes-external-ip

在上图中,节点1和节点2都有1个IP地址。节点1上的IP地址1.2.3.4绑定到实际Pod驻留在节点2中的httpd服务,而IP地址1.2.3.5绑定到实际Pod驻留在节点1中的nginx服务。底层的overlay网络使这成为可能。当我们curl 1.2.3.4时,应该看到来自httpd服务的响应,而curl 1.2.3.5时,则应该看到来自nginx服务的响应。

为什么不使用Ingress

即使Ingress也用于将服务公开给外部,但Ingress是为L7路由构建的。这意味着它构建为支持HTTP(端口80)和/或HTTPS(端口443)流量,而不支持其他端口。Ingress充当基于主机的路由,或类似于Web Server中的虚拟主机。一些能够为其他端口提供服务的ingress controllers,或者可能为L4路由提供了解决方法,但我从未真正尝试使用它们。

External IP的优缺点

使用External IP的优点是:

  • 您可以完全控制所使用的IP。您可以使用属于您的ASN的IP,而不要使用云提供商的ASN。

外部IP的缺点是:

  • 我们现在将要进行的简单设置并不是高可用的。这意味着,如果节点异常,则该服务将不再可用,您将需要手动修复该问题。
  • 管理IP需要做一些手工工作。IP不是为您动态配置的,因此需要人工干预。

如何使用External IP服务

同样,我们将使用与我们的群集设置相同的组网图,不同的是IP地址和主机名不同。这不是一个好例子,但是当我们验证设置时,很容易区分是哪个。在实际示例中,您可能希望在一个外部IP上公开MySQL DB,在另一个外部IP上公开Kafka群集。

kubernetes-external-ip-demo

我已为本教程配置了2个VM。k3s-external-ip-master将是我们的Kubernetes master节点,其IP为1.2.4.120。k3s-external-ip-worker将是Kubernetes worker节点,其IP为1.2.4.114。

步骤1:部署Kubernetes集群

让我们在主节点上安装k3s,然后让另一个节点加入集群。

1
2
$ k3sup install --ip <master node ip> --user <username>
$ k3sup join --server-ip <master node ip> --ip <worker node ip> --user <username>

您现在应该会看到类似的内容:

1
2
3
4
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k3s-external-ip-master Ready master 7m24s v1.16.3-k3s.2
k3s-external-ip-worker Ready <none> 2m21s v1.16.3-k3s.2

步骤2:创建Kubernetes Deployment资源

我们将创建nginx和httpd资源。

1
2
$ kubectl create deployment nginx --image=nginx
$ kubectl create deployment httpd --image=httpd

你现在应该看到这个:

1
2
3
4
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-86c57db685-fzxn5 1/1 Running 0 22s
httpd-7bddd4bd85-zk8ks 1/1 Running 0 16s

步骤3:将Deployment公开为External IP类型

让我们创建Nginx服务的yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat << EOF > nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
externalIPs:
- 1.2.4.114
EOF

创建httpd服务的yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat << EOF > httpd-service.yaml
apiVersion: v1
kind: Service
metadata:
name: httpd-service
spec:
selector:
app: httpd
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
externalIPs:
- 1.2.4.120
EOF

使用kubectl命令创建2个服务的yaml:

1
2
$ kubectl create -f nginx-service.yaml
$ kubectl create -f httpd-service.yaml

现在您的Kubernetes服务应该如下所示:

1
2
3
4
5
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 18m
httpd-service ClusterIP 10.43.240.149 1.2.4.120 80/TCP 32s
nginx-service ClusterIP 10.43.13.149 1.2.4.114 80/TCP 26s

您可能会在此处看到服务类型为ClusterIP。我不确定为什么它不显示“外部IP”。

k8s官网有说明,External IP与type无关

步骤4:瞧!

让我们curl httpd服务,您应该看到Apache默认页面。

1
2
3
4
5
6
7
8
9
10
$ curl -i 1.2.4.120
HTTP/1.1 200 OK
Date: Fri, 20 Dec 2019 03:36:23 GMT
Server: Apache/2.4.41 (Unix) <------
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Content-Type: text/html
<html><body><h1>It works!</h1></body></html>

接下来,curl nginx服务,您应该看到nginx默认页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ curl -i 1.2.4.114
HTTP/1.1 200 OK
Server: nginx/1.17.6 <------
Date: Fri, 20 Dec 2019 03:36:01 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
Connection: keep-alive
ETag: "5dd3e500-264"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
....

下一步是什么

浮动IP

如今,大多数云提供商都提供浮动IP服务。浮动IP允许您拥有1个IP,并将该IP动态分配给所需的任何IP。在这种情况下,可以将IP分配给Kubernetes集群中的任何工作节点。

在DigitalOcean(我相信其他提供商也允许这样做)中,您可以使用API调用将IP重新分配给其他VM。这意味着您可以在其他VM发生故障时迅速将其主动重新分配给其他VM,或者可以定期轮换IP。

kubernetes-floating-ip

从图中可以看到,我们有1个浮动IP 1.2.3.6,该IP首先分配给节点1,当节点1不可用时将切换到节点2。IP 1.2.3.6适用于我们的MySQL服务,并将放入我们的应用程序配置中。

我尚未尝试此设置,因此无法确认它是否有效。我将在以后的博客文章中更新结果。

任播IP

anycast

您可以将Anycast IP用作外部IP,以便它们具有高可用性。对于不熟悉Anycast IP的用户,这意味着1个IP可能会路由到2个或更多服务器。你可以在这里阅读更多。就个人而言,我不确定如何设置此设置。但是,我认为这在技术上是可行的。我认为这是运行外部IP服务的最佳方法。

结论

您可以通过很多选项为裸机Kubernetes集群获取IP。例如,您可以为此使用Inlets和MetalLB。此设置可能不是您的组织需要的最合适的设置。但是,很高兴知道如何使用此方法。

免责声明:我仅将其用于实验和测试,而本文不适用于生产。如果您打算在生产中使用它,请咨询您的解决方案架构师或CTO。

访问Kubernetes集群中托管的服务的两种最常见方法是通过IngressLoad Balancer。对于公有云用户来说,这些是访问服务的简单有效方法。云托管的控制器可以完成分配公共IP,设置负载平衡和管理SSL的繁重工作。

在本地运行私有托管的Kubernetes集群的运营商将很快意识到,将服务公开提供比在公有云上更为复杂。服务将在公共互联网上还是仅对本地用户公开?应该如何设置Ingress,负载平衡,IP分配和SSL管理?在维护安全性的同时将服务公开发布的最有效方法是什么?

Calico项目通过其服务IP发布功能为某些问题提供了答案,该功能与现有的机架(ToR)基础架构集成在一起,可以提供Kubernetes服务IP或Kubernetes外部服务IP的路由。在以下情况下,服务IP发布是一个很好的解决方案:

  • 您的ToR解决方案能够运行边界网关协议(BGP)
  • 您希望将服务从群集共享到网络基础结构的其余部分
  • 您想利用网络负载均衡

为了启用服务IP发布功能,Calico需要与BGP路由器建立对等关系,该路由器在Calico的内部路由器之外,但在网络本地。BGP是网络中使用的最基本的路由协议之一。在较高级别,BGP通过在信任对等方之间共享路由来工作。与ToR对等时,Calico共享Kubernetes服务的路由,这使它们可用于整个网络。

为了帮助讨论此功能,假定我们安装以下方式配置了网络和群集:

  • 托管Kubernetes节点的服务器机架通过顶部机架式路由器连接到物理网络
  • Calico作为CNI和网络策略插件运行
  • Calico和Rack顶部路由器配置为使用BGP对等

在此设置中,我们具有以下网络配置:

  • 机架顶部路由器的IP地址是192.168.1.1,服务器的IP地址是从192.168.1.0/24分配的
  • Kubernetes Pod网络配置了CIDR 10.48.0.0/16
  • Kubernetes服务集群IP范围配置为10.49.0.0/16
  • 外部服务IP范围配置为192.168.3.0/24

您的ToR BGP路由器的确切配置超出了本文的范围,并且会因您使用的供应商或软件包而异。如果您没有可用的服务器机架,但仍然想尝试该功能,那么在单独的服务器上运行的Bird Internet Routing Daemon(BIRD)是尝试进行操作的不错选择。

第一步是启用ToR和Calico网络之间的对等连接。其工作方式将根据您的ToR实现而有所不同,但是要牢记一些关键事项:

  • 必须将ToR路由器配置为与在每个节点上运行的Calico对等
  • ToR需要接受来自外部网络,外部服务网络和Pod服务网络的路由和流量
  • 如果可以选择,ToR应该启用正常重启,以防止网络服务中断

将ToR配置为接受路由后,下一步是在Calico端启用对等。首先通过以下清单告诉Calico有关外部BGP路由器的信息:

1
2
3
4
5
6
7
8
9
calicoctl apply -f - << EOF
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: bgppeer-global-64512
spec:
peerIP: 192.168.1.1
asNumber: 64512
EOF

启用对等功能后,Calico可以使Pod成为网络中的头等公民,而无需覆盖网络,并使他们可以直接在群集外部访问。

尽管启用了对等连接,但是Calico仍需要进一步配置以公开Kubernetes服务IP范围。这可以通过创建新的Calico BGPConfiguration资源来完成:

1
2
3
4
5
6
7
8
9
calicoctl create -f - <<EOF
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
serviceClusterIPs:
- cidr: 10.49.0.0/16
EOF

启用服务群集IP范围的发布后,ToR上的路由表将如下所示:

1
2
3
4
5
6
7
$ ip r
...
10.49.0.0/16
nexthop via 192.168.1.10 dev eth2 weight 1
nexthop via 192.168.1.11 dev eth2 weight 1
nexthop via 192.168.1.12 dev eth2 weight 1
...

注意到10.49.0.0/16网络的路由如何在节点之间实现ECMP负载平衡。您公开的所有服务将由ToR在所有节点上进行负载平衡。为了说明这一点,我们可以创建一个基本的Nginx服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
control:~$ kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
control:~$ kubectl expose nginx
control:~$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 19m
nginx ClusterIP 10.49.62.131 <none> 80/TCP 4m43s

现在,您可以从外部网络通过其内部群集IP地址访问Kubernetes托管服务。

1
2
3
4
5
6
external:~$ curl 10.49.62.131
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

这里有两个阶段的负载平衡。首先,ToR通过将其路由到节点来对与群集IP的连接进行负载平衡。然后,使用NAT(网络地址转换)在该节点上运行的kube-proxy负载均衡到特定的Pod,以将目标IP从群集IP更改为后备Pod之一的IP地址。后备Pod可能在本地节点上,也可能在其他节点之一上,从而导致了另一个网络跃点。

如果我们想避免额外的潜在网络跳数,可以通过将服务上的外部流量策略设置为本地来实现。

1
2
control:~$ kubectl patch service nginx \
-p '{"spec":{"type": "NodePort", "externalTrafficPolicy":"Local"}}'

现在,在ToR上,您将看到路由表的新增内容,包括Nginx服务到正在运行Nginx的特定节点的ECMP负载平衡:

1
2
3
4
5
6
7
8
9
10
$ ip r
...
10.49.0.0/16
nexthop via 192.168.2.10 dev eth2 weight 1
nexthop via 192.168.2.11 dev eth2 weight 1
nexthop via 192.168.2.12 dev eth2 weight 1
...
10.49.62.131
nexthop via 192.168.2.11 dev eth2 weight 1
nexthop via 192.168.2.12 dev eth2 weight 1

这是公开Kubernetes网络服务的简便方法,但是在操作上,它具有将Kubernetes群集中该IP范围内的每个服务都暴露给网络其余部分的缺点。如果您想更精细地控制提供哪些服务,或者想要分配真正面向公众的IP地址,Calico可以通过发布外部服务IP来解决这个问题。该方法类似,主要区别在于外部IP不由Kubernetes集群管理,必须手动分配给服务。

下一个示例将说明这一点。首先重新配置BGPConfiguration,以发布外部IP而不是内部IP(值得注意的是,您可以一次公开这两个集合,但是在此示例中,我们要关闭对内部网络的公共访问,同时仍提供对应用程序的访问)。

1
2
3
4
5
6
7
8
9
10
control :~$calicoctl create -f - <<EOF
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
serviceClusterIPs:
serviceExternalIPs:
- cidr: 192.168.3.0/24
EOF

检查路由时,请注意已删除到群集IP范围的路由,并已添加到外部服务网络的路由。

1
2
3
4
5
6
7
$ ip r
...
192.168.3.0/24
nexthop via 192.168.1.10 dev eth2 weight 1
nexthop via 192.168.1.11 dev eth2 weight 1
nexthop via 192.168.1.12 dev eth2 weight 1
...

该服务的群集IP不再公开可见。

1
2
external :~$ curl -m 10 10.49.62.131
curl: (28) Connection timed out after 10001 milliseconds

我们现在在发布外部IP范围,但我们还需要为服务分配一个外部IP:

1
2
3
4
5
6
control:~$ kubectl patch svc nginx \
-p '{"spec": {"externalIPs": ["192.168.3.180"]}}'
control:~$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 152m
nginx NodePort 10.49.62.131 192.168.3.180 80:31890/TCP 109m

检查连接性:

1
2
3
4
5
6
external:~$ curl 192.168.3.180
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

到此为止,我们快速完成了Calico如何在Kuberentes群集之外发布服务和外部服务IP的快速浏览。对于具有启用BGP路由的本地云,这是一种简单的解决方案,无需提供安装和维护自定义Kubernetes负载均衡器或Ingress控制器的额外工作即可访问Kubernetes服务。如果您想了解更多有关此功能的信息,请查阅官方的Calico项目”Advertise Kubernetes Service IPs“。

Linux具有丰富的虚拟网络功能,可用作托管VM和容器以及云环境的基础。本文将简要介绍所有常用的虚拟网络接口类型。内容没有代码分析,仅简要介绍了接口及其在Linux上的用法。可以使用ip link help命令获取接口列表的帮助。

内容涵盖了以下常用的接口以及一些易于相互混淆的接口:

  • Bridge
  • Bonded interface
  • Team device
  • VLAN (Virtual LAN)
  • VXLAN (Virtual eXtensible Local Area Network)
  • MACVLAN
  • IPVLAN
  • MACVTAP/IPVTAP
  • MACsec (Media Access Control Security)
  • VETH (Virtual Ethernet)
  • VCAN (Virtual CAN)
  • VXCAN (Virtual CAN tunnel)
  • IPOIB (IP-over-InfiniBand)
  • NLMON (NetLink MONitor)
  • Dummy interface
  • IFB (Intermediate Functional Block)
  • netdevsim

阅读本文之后,我们将了解这些接口是什么,它们之间的区别,何时使用它们以及如何创建它们。

Bridge

Linux网桥的行为类似于网络交换机。它在与其连接的接口之间转发数据包。它通常用于在路由器,网关或虚拟机与主机上的网络命名空间之间转发数据包。它还支持STP,VLAN过滤和多播侦听。

bridge

如果想要在VM,容器和主机之间建立通信通道,请使用网桥。

以下是创建网桥的方法:

1
2
3
4
5
# ip link add br0 type bridge
# ip link set eth0 master br0
# ip link set tap1 master br0
# ip link set tap2 master br0
# ip link set veth1 master br0

上述命令创建了一个名为br0网桥,并且设置了两个TAP设备(tap1tap2),一个VETH设备(veth1),和物理设备(eth0)作为它的从设备,如上图所示。

Bond

Linux bond驱动提供了一种用于将多个网络接口聚合为单个逻辑“绑定”接口的方法。绑定接口的行为取决于模式。一般来说,提供热备用或负载平衡两种模式。

bond

如果想提高链接速度或在服务器上进行故障转移时,请使用bond接口。

以下是创建bond接口的方法:

1
2
3
ip link add bond1 type bond miimon 100 mode active-backup
ip link set eth0 master bond1
ip link set eth1 master bond1

上述命令创建了一个名称为bond1active-backup模式的bond接口。对于其他模式,请参阅 内核文档

Team device

与bond接口类似,team device的目的是提供一种在L2层将多个NIC(端口)分组为一个逻辑端口(teamdev)的机制。

team

需要注意的是,team device不会尝试复制或模仿bond接口。它的作用是使用不同的方法来解决相同的问题。

但是bond和team device之间也存在一些功能差异。例如,team device支持LACP负载平衡,NS/NA(IPV6)链接监视,D-Bus接口等,而这些功能在bond中是不存在的。有关bond和team device之间差异的更多详细信息,请参见 bond vs team device

综上,如果想使用bond无法提供的某些功能,请使用team device。

创建team device的方法如下:

1
2
3
4
5
# teamd -o -n -U -d -t team0 -c '{"runner": {"name": "activebackup"},"link_watch": {"name": "ethtool"}}'
# ip link set eth0 down
# ip link set eth1 down
# teamdctl team0 port add eth0
# teamdctl team0 port add eth1

上述命令将创建一个team0的team device,且模式为active-backup,并添加eth0eth1作为team0的子接口。

最近,一个名为net_failover的新驱动已添加到Linux内核。它是用于虚拟化的另一种故障转移网络设备,并管理主([passthru/VF虚拟功能] 设备)从网络设备和备用(原始超虚拟接口)从网络设备。

net_failover

VLAN

VLAN(也称为虚拟LAN)通过向网络数据包添加标签来分隔广播域。VLAN使网络管理员可以将同一台交换机下或不同交换机之间的主机分组。

VLAN标头如下所示:

vlan_01

如果想在VM,命名空间或主机中分隔子网,请使用VLAN。

以下是创建VLAN的方法:

1
2
# ip link add link eth0 name eth0.2 type vlan id 2
# ip link add link eth0 name eth0.3 type vlan id 3

上述命令将添加名为eth0.2VLAN2和名为eth0.3VLAN 3 。拓扑如下所示:

vlan

注意:在配置VLAN时,需要确保连接到主机的交换机能够处理VLAN标签,例如,通过将交换机端口设置为中继模式。

VXLAN

VXLAN(虚拟可扩展局域网)是一种隧道协议,旨在解决IEEE 802.1q中有限的VLAN ID(4,096)的问题。由IETF RFC 7348描述。

通过24位网段ID(又称为VXLAN网络标识符(VNI)),VXLAN最多允许2 ^ 24(16,777,216)个虚拟LAN,这是VLAN容量的4,096倍。

VXLAN将带有VXLAN标头的第2层帧封装到UDP-IP数据包中,如下所示:

vxlan_01

VXLAN通常部署在虚拟主机上的数据中心中,该主机可以分布在多个机架上。

vxlan

下面是使用VXLAN的方法:

1
# ip link add vx0 type vxlan id 100 local 1.1.1.1 remote 2.2.2.2 dev eth0 dstport 4789

作为参考,可以阅读VXLAN内核文档VXLAN简介

MACVLAN

使用VLAN,我们可以在一个接口上创建多个接口,并根据VLAN标记过滤数据包。使用MACVLAN,我们可以在一个接口上创建具有不同第2层(即以太网MAC)地址的多个接口。

在使用MACVLAN之前,如果要从VM或命名空间连接到物理网络,则需要创建TAP/VETH设备,并将一侧连接到网桥,并同时将物理接口连接到主机上的网桥,如下所示。

br_ns

现在,借助MACVLAN,我们可以将与MACVLAN关联的物理接口直接绑定到命名空间,而无需桥接。

macvlan

MACVLAN有五种类型:

1.Private:即使外部交换机支持hairpin模式,也不允许同一物理接口上的MACVLAN实例之间进行通信。

macvlan_01

  1. VEPA:同一物理接口上从一个MACVLAN实例到另一个MACVLAN实例的数据通过该物理接口传输。连接的交换机需要支持hairpin模式,或者必须有TCP/IP路由器转发数据包才能进行通信。

macvlan_02

  1. Bridge:所有端点都通过物理接口通过简单的桥接器直接相互连接。

macvlan_03

  1. Passthru:允许将单个VM直接连接到物理接口。

macvlan_04

  1. Source:该模式用于基于允许的源MAC地址列表过滤流量,以创建基于MAC的VLAN关联。请参阅提交消息

模式是根据不同的需求选择的。桥接模式是最常用的。如果要从容器直接连接到物理网络时,请使用MACVLAN。

设置MACVLAN的方法如下:

1
2
3
4
5
6
# ip link add macvlan1 link eth0 type macvlan mode bridge
# ip link add macvlan2 link eth0 type macvlan mode bridge
# ip netns add net1
# ip netns add net2
# ip link set macvlan1 netns net1
# ip link set macvlan2 netns net2

上述命令将以桥接模式创建两个新的MACVLAN设备,并将这两个设备分配给两个不同的命名空间。

IPVLAN

IPVLAN与MACVLAN相似,区别在于端点具有相同的MAC地址。

ipvlan

IPVLAN支持L2和L3模式。IPVLAN L2模式在桥接模式下的行为类似于MACVLAN。父接口看起来像一个网桥或交换机。

ipvlan_01

在IPVLAN L3模式下,父接口的行为就像路由器,并且数据包在端点之间路由,从而提供了更好的可伸缩性。

ipvlan_02

关于何时使用IPVLAN, IPVLAN内核文档 说MACVLAN和IPVLAN在许多方面都非常相似,并且可以依据特定的场景很好地定义选择哪个。如果以下情况之一定义我们需要的场景,则可以选择使用ipvlan:
(a)连接到外部交换机/路由器的Linux主机已配置了策略,每个端口仅允许一个mac。
(b)在主服务器上创建的虚拟设备均未超过mac容量,并且无法将NIC置于混杂模式,因此性能下降是一个问题。
(c)如果要将从设备放入敌对/不受信任的网络命名空间中,则从设备上的L2可能会被更改/滥用。

设置IPVLAN实例的方法如下:

1
2
3
# ip netns add ns0
# ip link add name ipvl0 link eth0 type ipvlan mode l2
# ip link set dev ipvl0 netns ns0

上述命令将创建一个名为ipvl0L2的IPVLAN设备,并分配给ns0命名空间。

MACVTAP/IPVTAP

MACVTAP/IPVTAP是一种新的设备驱动,旨在简化虚拟化桥接网络。当在物理接口顶部创建MACVTAP/IPVTAP实例时,内核还将创建一个字符设备/dev/tapX,以与TUN/TAP设备一样使用,并可以由KVM / QEMU直接使用。

使用MACVTAP/IPVTAP,我们可以用单个模块替换TUN/TAP和网桥驱动的组合:

macvtap

通常,MACVLAN/IPVLAN用于使访客和主机都直接显示在主机所连接的交换机上。MACVTAP和IPVTAP之间的差异与MACVLAN/IPVLAN相同。

以下是创建MACVTAP实例的方法:

1
# ip link add link eth0 name macvtap0 type macvtap

MACsec

MACsec(媒体访问控制安全)是用于有线以太网LAN中安全性的IEEE标准。与IPsec相似,作为第2层规范,MACsec不仅可以保护IP流量,还可以保护ARP,NS(邻居发现)和DHCP。MACsec标头如下所示:

macsec_01

MACsec的主要用例是保护标准LAN上的所有消息(包括ARP,NS和DHCP消息)的安全。

macsec

MACsec配置的方法如下:

1
# ip link add macsec0 link eth1 type macsec

注意:上述命令仅在eth1设备上添加了名为macsec0的MACsec。有关更详细的配置,请参阅Sabrina DubrocaMACsec简介“配置示例”部分 。

VETH

VETH(虚拟以太网)设备是本地以太网隧道。设备是成对创建的,如下图所示。在VETH对中的一个设备上传输的数据包将立即在另一设备上接收。当任何一台设备关闭时,该VETH对的链接状态为关闭。

veth

当任意命名空间需要与主机命名空间或彼此之间进行通信时,请使用VETH配置。

以下是设置VETH配置的方法:

1
2
3
# ip netns add net1
# ip netns add net2
# ip link add veth1 netns net1 type veth peer name veth2 netns net2

上述命令将创建两个名称空间net1net2,以及一对VETH设备,并将veth1分配给net1veth2分配给net2。这两个命名空间与此VETH对相连。分配一对IP地址,这样就可以在两个命名空间之间通信。

VCAN

与网络环回设备类似,VCAN(虚拟CAN)驱动提供了虚拟本地CAN(控制器局域网)接口,因此用户可以通过VCAN接口发送/接收CAN消息。如今,CAN主要用于汽车领域。

有关更多CAN协议信息,请参考 内核CAN文档

如果需要在本地主机上测试CAN协议实现时,请使用VCAN。

创建VCAN的方法如下:

1
# ip link add dev vcan1 type vcan

VXCAN

与VETH驱动类似,VXCAN(虚拟CAN隧道)在两个VCAN网络设备之间实现本地CAN流量隧道。创建VXCAN实例时,两个VXCAN设备将成对创建。当一端接收到数据包时,该数据包出现在设备对上,反之亦然。VXCAN可用于跨命名空间通信。

如果想跨命名空间发送CAN消息时,请使用VXCAN配置。

设置VXCAN实例的方法如下:

1
2
3
# ip netns add net1
# ip netns add net2
# ip link add vxcan1 netns net1 type vxcan peer name vxcan2 netns net2

注意:Red Hat Enterprise Linux尚不支持VXCAN。

IPOIB

IPOIB设备支持IP-over-InfiniBand协议。这将通过InfiniBand(IB)传输IP数据包,因此我们可以将IB设备用作快速NIC。

IPoIB驱动支持两种操作模式:datagram和connected。在datagram模式下,使用IB UD(不可靠数据包)传输。在connected模式下,使用IB RC(可靠连接)传输。connected模式利用了IB传输的连接特性,并允许MTU最多达到64K的最大IP数据包大小。

有关更多详细信息,请参见 IPOIB内核文档

如果想拥有IB设备并想通过IP与远程主机通信时,请使用IPOIB设备。

以下是创建IPOIB设备的方法:

1
# ip link add ib0 name ipoib0 type ipoib pkey IB_PKEY mode connected

NLMON

NLMON是Netlink监视设备。

如果想要监视系统Netlink消息时,请使用NLMON设备。

以下是创建NLMON设备的方法:

1
2
3
# ip link add nlmon0 type nlmon
# ip link set nlmon0 up
# tcpdump -i nlmon0 -w nlmsg.pcap

上述命令将创建一个名称为NLMON的nlmon0设备并进行设置。使用数据包嗅探器(例如, tcpdump)捕获Netlink消息。Wireshark的最新版本具有对Netlink消息进行解码的功能。

Dummy interface

虚拟接口完全是虚拟的,例如loopback接口。虚拟接口的目的是提供一种设备,可以在不实际传输数据包的情况下路由数据包。

使用虚拟接口使无效的SLIP(串行Internet协议)地址看起来像本地程序的真实地址。如今,虚拟接口主要用于测试和调试。

以下是创建虚拟接口的方法:

1
2
3
# ip link add dummy1 type dummy
# ip addr add 1.1.1.1/24 dev dummy1
# ip link set dummy1 up

IFB

IFB(中间功能块)驱动提供了一种设备,该设备允许集中来自多个源的流量并调整传入流量,而不是将其丢弃。

如果想要排队和调整传入流量时,请使用IFB接口。

以下是创建IFB接口的方法:

1
2
3
4
5
# ip link add ifb0 type ifb
# ip link set ifb0 up
# tc qdisc add dev ifb0 root sfq
# tc qdisc add dev eth0 handle ffff: ingress
# tc filter add dev eth0 parent ffff: u32 match u32 0 0 action mirred egress redirect dev ifb0

上述命令将创建一个名为ifb0的IFB设备,并将根据qdisc调度程序替换为SFQ(随机公平队列),SFQ是无类排队调度程序。然后,在eth0上添加一个qdisc调度程序,并将所有入口流量重定向到ifb0

有关更多IFB qdisc用例,请参考 IFB上的Linux Foundation Wiki

netdevsim接口

netdevsim是一种模拟的联网设备,用于测试各种联网API。目前,它特别专注于测试硬件卸载,tc/XDP BPF和SR-IOV。

如下命令可以创建一个netdevsim设备

1
2
# ip link add dev sim0 type netdevsim
# ip link set dev sim0 up

要启用tc卸载:

1
# ethtool -K sim0 hw-tc-offload on

加载XDP BPF或tc BPF程序:

1
# ip link set dev sim0 xdpoffload obj prog.o

要添加用于SR-IOV测试的VF,请执行以下操作:

1
2
# echo 3 > /sys/class/net/sim0/device/sriov_numvfs
# ip link set sim0 vf 0 mac

要更改vf编号,需要先完全禁用它们:

1
2
# echo 0 > /sys/class/net/sim0/device/sriov_numvfs
# echo 5 > /sys/class/net/sim0/device/sriov_numvfs

注意:默认情况下,RHEL中没有编译netdevsim。

Linux支持多种隧道,但是新用户可能会因为它们之间的差异而感到困惑,无法确定哪种隧道适合给定的场景。本文将简要介绍Linux内核中常用的隧道接口。内容没有代码分析,仅简要介绍了接口及其在Linux上的用法。感兴趣的话,可以通过iproute2的命令ip link help获得隧道接口列表以及特定隧道配置的帮助。

这篇文章介绍了以下常用接口:

  • IPIP
  • SIT
  • ip6tnl
  • VTI和VTI6
  • GRE和GRETAP
  • IP6GRE和IP6GRETAP
  • FOU
  • GUE
  • GENEVE
  • ERSPAN和IP6ERSPAN

阅读本文之后,我们将了解这些接口是什么,它们之间的区别,何时使用它们以及如何创建它们。

IPIP

顾名思义,IPIP隧道是RFC 2003中定义的IP over IP隧道。IPIP隧道标头如下所示:

ipip

通常用于通过公共IPv4互联网连接两个内部IPv4子网。它具有最低的开销,但只能传输IPv4单播流量。这意味着您无法通过IPIP隧道发送多播。

IPIP隧道同时支持IP over IP和MPLS over IP。

注意:加载ipip模块或首次创建IPIP设备时,Linux内核将在每个名称空间中创建tunl0默认设备,其属性为local = any和remote = any。接收IPIP协议数据包时,如果内核找不到其他本地/远程属性与其来源或目标地址更匹配的设备,则内核会将其转发给tunl0作为备用设备。

如下是创建IPIP隧道的方法:

1
2
3
4
5
6
7
8
9
10
11
12
On Server A:
# ip link add name ipip0 type ipip local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set ipip0 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev ipip0
Add a remote internal subnet route if the endpoints don't belong to the same subnet
# ip route add REMOTE_INTERNAL_SUBNET/24 dev ipip0

On Server B:
# ip link add name ipip0 type ipip local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set ipip0 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev ipip0
# ip route add REMOTE_INTERNAL_SUBNET/24 dev ipip0

注意:请根据测试环境将LOCAL_IPv4_ADDR,REMOTE_IPv4_ADDR,INTERNAL_IPV4_ADDR,REMOTE_INTERNAL_SUBNET替换为地址。

SIT

SIT代表“简单Internet过渡”。主要目的是互连位于全球IPv4互联网中的隔离IPv6网络。

最初,它只有IPv6 over IPv4隧道模式。然而,经过多年的发展,它获得了对多种不同模式的支持,例如ipip(与IPIP隧道相同),ip6ip,mplsip等。模式any用于同时接受IP和IPv6流量,这在某些部署中可能很有用。 SIT隧道还支持ISATA,用法见示例

SIT隧道报文头看起来如下所示:

sit.png

加载sit模块后,Linux内核将创建一个默认设备,名为sit0。

如下是创建SIT隧道的方法:

1
2
3
4
On Server A:
# ip link add name sit1 type sit local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR mode any
# ip link set sit1 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev sit1

然后,在远端执行相同的步骤。

ip6tnl

ip6tnl是基于IPv6的IPv4/IPv6隧道接口,看起来像SIT隧道的IPv6版本。报文头看起来如下所示:

ip6tnl.png

ip6tnl支持ip6ip6,ipip6,和any模式。ipip6模式是IPv4 over IPv6,ip6ip6模式是IPv6 over IPv6,any模式支持IPv4/IPv6 over Pv6。

加载ip6tnl模块后,Linux内核将创建一个名为ip6tnl0的默认设备。

如下是创建ip6tnl隧道的方法:

1
# ip link add name ipip6 type ip6tnl local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR mode any

VTI和VTI6

Linux上的虚拟隧道接口(VTI)与Cisco的VTI和Juniper的安全隧道(st.xx)类似。

这个特定的隧道驱动程序实现IP封装,可以与xfrm一起使用以提供安全隧道的概念,然后在其之上使用内核路由。

通常,VTI隧道的运行方式几乎与ipip或sit隧道相同,不同之处在于,它们添加了fwmark和IPsec封装/解封装。

VTI6是VTI的IPv6实现。

如下是创建VTI隧道的方法:

1
2
3
4
5
6
7
8
# ip link add name vti1 type vti key VTI_KEY local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set vti1 up
# ip addr add LOCAL_VIRTUAL_ADDR/24 dev vti1

# ip xfrm state add src LOCAL_IPv4_ADDR dst REMOTE_IPv4_ADDR spi SPI PROTO ALGR mode tunnel
# ip xfrm state add src REMOTE_IPv4_ADDR dst LOCAL_IPv4_ADDR spi SPI PROTO ALGR mode tunnel
# ip xfrm policy add dir in tmpl src REMOTE_IPv4_ADDR dst LOCAL_IPv4_ADDR PROTO mode tunnel mark VTI_KEY
# ip xfrm policy add dir out tmpl src LOCAL_IPv4_ADDR dst REMOTE_IPv4_ADDR PROTO mode tunnel mark VTI_KEY

也可以通过libreswan或strongSwan配置IPsec。

GRE和GRETAP

通用路由封装,也称为GRE,在RFC 2784中定义。

GRE隧道在内部和外部IP头之间添加了一个额外的GRE头。从理论上讲,GRE可以封装具有有效以太网类型的任何第3层协议,而IPIP只能封装IP。GRE报文头看起来如下所示:

gre

请注意,GRE隧道支持传输多播流量和IPv6报文。

gre模块被加载,Linux内核将创建一个默认设备,命名gre0

如下是创建GRE隧道的方法:

1
# ip link add name gre1 type gre local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR [seq] key KEY

GRE隧道在OSI第3层上运行,而GRETAP在OSI第2层上运行,这意味着内部头中有一个以太网头部。

gretap

如下是创建GRETAP隧道的方法:

1
# ip link add name gretap1 type gretap local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR

IP6GRE和IP6GRETAP

IP6GRE是GRE的IPv6实现,它使我们能够封装基于IPv6的任何第3层协议。隧道头看起来如下所示:

gre6

与GRETAP一样,IP6GRETAP在内部标头中具有以太网头部:

gre6tap

如下是创建GRE隧道的方法:

1
2
# ip link add name gre1 type ip6gre local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR
# ip link add name gretap1 type ip6gretap local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR

FOU

隧道可以在网络堆栈的多个级别上发生。IPIP,SIT,GRE隧道位于IP级别,而FOU(UDP上的foo)是UDP级别的隧道。

使用UDP隧道具有一些优点,因为UDP与现有的硬件基础结构一起工作,例如NIC中的RSS,交换机中的ECMP。开发人员的验证结果显示SIT和IPIP协议的性能显著提高。

当前,FOU隧道支持基于IPIP,SIT,GRE的封装协议。FOU报文头示例如下所示:

fou

如下是创建FOU隧道的方法:

1
2
# ip fou add port 5555 ipproto 4
# ip link add name tun1 type ipip remote 192.168.1.1 local 192.168.1.2 ttl 225 encap fou encap-sport auto encap-dport 5555

第一条命令为绑定到5555的IPIP配置了FOU接收端口;对于GRE,需要设置ipproto 47

第二条命令使用目标端口5555设置了用于FOU封装的新IPIP虚拟接口(tun1)。

注意:Red Hat Enterprise Linux不支持FOU。

GUE

通用UDP封装(GUE)是另一种UDP隧道。FOU和GUE之间的区别在于GUE具有自己的封装头,其中包含协议信息和其他数据。

当前,GUE隧道支持内部IPIP,SIT,GRE封装。GUE标头示例如下所示:

gue

如下是创建GUE隧道的方法:

1
2
# ip fou add port 5555 gue
# ip link add name tun1 type ipip remote 192.168.1.1 local 192.168.1.2 ttl 225 encap gue encap-sport auto encap-dport 5555

这将为绑定到5555的IPIP设置一个GUE接收端口,并为GUE封装配置一个IPIP隧道。

注意:Red Hat Enterprise Linux不支持GUE。

GENEVE

通用网络虚拟化封装(GENEVE)支持VXLAN,NVGRE和STT的所有功能,旨在克服它们的局限性。许多人认为GENEVE最终可以完全取代这些早期格式。隧道头看起来如下所示:

geneve

看起来非常类似于VXLAN。主要区别在于GENEVE报文头是灵活的。通过使用新的Type-Length-Value(TLV)字段扩展标头来添加新功能非常容易。有关更多详细信息,可以查看最新的ietf草案或参考什么是GENEVE?

开放式虚拟网络(OVN)使用GENEVE作为默认封装。

如下是创建GENEVE隧道的方法:

1
# ip link add name geneve0 type geneve id VNI remote REMOTE_IPv4_ADDR

ERSPAN和IP6ERSPAN

封装的远程交换端口分析器(ERSPAN)使用GRE封装将基本的端口镜像功能从第2层扩展到第3层,从而允许通过可路由的IP网络发送镜像的流量。ERSPAN标头看起来如下所示:

erspan

ERSPAN隧道允许Linux主机充当ERSPAN流量源,并将ERSPAN镜像流量发送到远程主机或ERSPAN目标,后者接收并解析从Cisco或其他具有ERSPAN功能的交换机生成的ERSPAN数据包。此设置可用于分析,诊断和检测恶意流量。

Linux当前支持两个ERSPAN版本的大多数功能:v1(类型II)和v2(类型III)。

如下是创建ERSPAN隧道的方法:

1
2
3
4
5
6
7
# ip link add dev erspan1 type erspan local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR seq key KEY erspan_ver 1 erspan IDX
or
# ip link add dev erspan1 type erspan local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR seq key KEY erspan_ver 2 erspan_dir DIRECTION erspan_hwid HWID

Add tc filter to monitor traffic
# tc qdisc add dev MONITOR_DEV handle ffff: ingress
# tc filter add dev MONITOR_DEV parent ffff: matchall skip_hw action mirred egress mirror dev erspan1

概括

下面是我们介绍的所有隧道的总结。

隧道/链接类型 外部报文头 封装报文头 内部报文头
ipip IPv4 None IPv4
sit IPv4 None IPv4 / IPv6
ip6tnl IPv6 None IPv4 / IPv6
vti IPv4 IPsec IPv4
vti6 IPv6 IPsec IPv6
gre IPv4 GRE IPv4 / IPv6
gretap IPv4 GRE 以太网+ IPv4 / IPv6
ip6gre IPv6 GRE IPv4 / IPv6
ip6gretap IPv6 GRE 以太网+ IPv4 / IPv6
fou IPv4 / IPv6 UDP IPv4 / IPv6 / GRE
gue IPv4 / IPv6 UDP + GUE IPv4 / IPv6 / GRE
geneve IPv4 / IPv6 UDP +geneve 以太网+ IPv4 / IPv6
erspan IPv4 GRE + ERSPAN IPv4 / IPv6
ip6erspan IPv6 GRE + ERSPAN IPv4 / IPv6

注意:本教程中的所有配置都是临时生效的,并且在服务器重新启动后会丢失。如果要使配置在重新引导后保持不变,请考虑使用网络配置守护程序,例如NetworkManager或特定于发行版的机制。

okteto介绍

Okteto是一个基于Kubernetes的程序开发平台,通过直接在Kubernetes中开发和测试代码来构建应用程序。截止目前,该平台提供免费部署docker容器的服务,免费容量为8G内存,4核CPU和50G磁盘空间。

image

申请步骤

  1. 进入okteto官网,点击Login。

    image

  2. 通过github账户登录。

    image

  3. 登录成功后,可以看到控制台界面。

    image

  4. 点击Deploy可以选择部署预定义的应用,或者选择部署容器。

    image

    image

部署hello-world应用

  1. 打开hello-world应用配置页面,根据默认参数部署即可。

image

  1. 等待一段时间,查看状态为running。

    image

    1. 通过应用提供的rest接口访问,验证部署成功。

    image

自开始构建Okteto以来,我们一直在与许多开发人员进行交流。每个团队都有非常独特的挑战,但是经常出现的一个挑战是将一行代码从开发人员的IDE移植到Kubernetes需要多长时间。团队要么必须手动进行所有操作(构建,标记,推送,重新部署),要么将其更改推送到CI/CD服务,并等待直到更改被处理为止。

无论哪种方式,部署变更的时间都会打断您的流程,您的生产力因此而下降。这让我们思考:为什么开发人员需要了解整个构建部署基础结构才能验证特定于应用程序的更改?

我们很高兴宣布推出Okteto Push工具,这是将代码更改推送到Kubernetes的最快方法。一个直接在Kubernetes中自动构建,推送和更新应用程序的命令。

推送代码

okteto push的目标是消除开发人员在开发应用程序时需要执行的所有操作。编码,推送并实时查看更改。这样,我们所有人都可以专注于我们的应用程序,而不必担心底层的传递途径。

为了使用okteto push,我们需要的是包含代码,Dockerfile和最新版本Okteto CLI的本地文件夹。

首先,运行okteto login在Okteto Cloud中创建一个免费帐户。这将允许您使用Okteto构建服务直接在云中构建镜像。

1
$ okteto login

运行okteto push命令将代码推送到Kubernetes,将$IMAGE替换为自己的镜像仓库(例如pchico83/hello-world):

1
$ okteto push --name hello-world -t $IMAGE
1
2
3
4
5
6
7
 i  Running your build in Okteto Cloud...
[+] Building 2.8s (16/16) FINISHED
=> importing cache manifest from pchico83/hello-world:okteto 1.1s
...
...
...
✓ Source code pushed to the development environment 'hello-world'

okteto push将使用Okteto Build Service自动构建开发镜像,生成一个开发标签,将其推送到容器仓库中并将其部署到Kubernetes。如果已经在运行一个应用程序,它将自动找到该deployment 并将镜像替换为新的。如果没有,它甚至可以创建deployment和service。

一个简单的命令即可在几秒钟内将代码导入任何Kubernetes环境。无需在本地运行docker,选择容器标签或修改yaml文件即可更新应用。

学习更多

Okteto Push可与任何编程语言,框架或平台一起使用。在我们的ASP.NETGoJavaNode.jsPythonPHPRuby入门指南中了解更多信息。

下一步

在Okteto,我们致力于使每个开发人员都可以访问Kubernetes,并将内部循环开发周期减少到最小。Okteto Push仍然要求开发人员拥有Dockerfile,但是我们正在尝试使用Cloud Buildpacks和类似技术来完全消除这一要求。

结论

Okteto社区中的一些人在过去几周内一直在内部使用okteto push,到目前为止,反馈非常好。我们已经看到人们将其用作内部流程的一部分,作为提交前的验证机制,以创建预览环境,甚至用于产品部署。真的很令人兴奋!