架构师必知必会系列:容器网络与服务发现

发布于:2023-10-25 ⋅ 阅读:(84) ⋅ 点赞:(0)

作者:禅与计算机程序设计艺术

1.背景介绍

容器(Container)技术逐渐成为主流云计算技术,Kubernetes作为容器编排工具,已经成为各大云厂商、社区推崇的“标杆”容器平台。Kubernetes提供应用的调度和部署能力,通过动态分配资源和存储,实现业务自动伸缩,保障了容器集群的高可用性。但是由于容器技术发展迅速,容器网络、存储等领域也同时发展起来,给 Kubernetes 提供了更加强大的网络能力。

容器网络是指如何让不同容器可以互相访问、通信,主要分为三种类型:

  • 联通性(Connectivity):容器间如何建立网络连接?
  • 负载均衡(Load balancing):多个容器之间如何做负载均衡?
  • 命名服务(Naming services):容器是否需要基于名称进行访问?

服务发现(Service Discovery)是一个微服务架构中非常重要的一环,它的作用是定位到特定的服务实例,并将请求路由到该实例上。目前业界已有众多服务发现方案,包括基于 DNS 的服务发现、基于注册中心的服务发现、基于分布式缓存的服务发现等。本文从这两个方面,分别分析一下容器网络和服务发现的相关技术要点及其运作方式。

2.核心概念与联系

2.1 容器网络

2.1.1 Docker网络模式

Docker提供了五种网络模式(Network Mode),可用于容器间的网络互连。

  1. bridge 模式:默认的网络模式,docker启动时会创建一个名为 docker0 的网桥,docker0网桥与主机系统运行在同一个网卡上,因此该模式下的容器可以直接通过主机系统的IP地址相互通信。
  2. host 模式:容器和宿主机共用一个网络命名空间,所有端口对外部都是可见的。
  3. none 模式:不配置任何网络,容器之间不建立任何网络连接。
  4. container:<name|id> 模式:容器间建立虚拟网卡,并将指定容器加入到当前容器的网络栈。
  5. overlay 模式:使用Overlay网络进行跨主机容器间的通信,例如Swarm模式下的容器网络。

除了以上五种网络模式之外,用户还可以自定义网络驱动,通过第三方插件扩展出更多的网络模式。

2.1.2 容器网络模型

在Veth Pair模型下,每个容器都有一个对应的Endpoint设备,Endpoint设备是由Linux内核创建的,用来建立Linux网络命名空间中的一个虚拟网卡。容器A和容器B共享相同的Namespace(即进程级隔离),并且它们各自独立地获得了一个VethPair设备,VethPair设备是一对虚拟网卡,分别属于两个容器。VethPair设备的名称一般为vethXXX和vethYYY,其中XXX和YYY的编号是随机生成的。

当容器A想与容器B通信时,它会将自己的VethPair设备XXX和容器B的VethPair设备YYY连接在一起。这样,容器A和容器B就都在一个共享的网络命名空间中,容器A可以通过容器B的IP地址与容器B通信。

容器网络带来的主要好处包括:

  1. 容器之间直接可达,无需额外配置,只需要两端连接;
  2. 没有单独配置IP,IP由系统自动分配;
  3. 支持容器间的远程访问,允许不同主机上的容器互相访问。

然而,容器网络也存在一些局限性:

  1. IP地址分配难以管理,难以确定哪个容器对应哪些IP地址;
  2. 容易出现性能瓶颈,尤其是在容器数量很多的时候;
  3. Veth Pair模型不支持多播、广播等协议。

为了解决这些问题,Kubernetes提供了更加完善的网络模型,包括Pod网路(CNI Plugin)、Service网路(kube-proxy)等。后续章节将详细介绍这两种网络模型。

2.2 服务发现

服务发现是微服务架构的一个重要组成部分。服务发现旨在帮助客户端快速找到服务的位置,客户端通过服务发现模块查找到服务的IP地址、端口号等信息,然后就可以直接向服务发送请求,而不需要知道服务在哪些机器上运行或者它们的具体配置。

服务发现通常分为两大类:静态服务发现和动态服务发现。

  1. 静态服务发现:静态服务发现指的是把服务节点的信息配置到配置文件中,这种方式显得过于静态,不方便服务扩容和修改。
  2. 动态服务发现:动态服务发现则是利用注册中心实时感知服务的变化,从而动态地更新服务的节点信息。注册中心往往采用中心化的方式,服务器之间互相保持联系,只要其中一台服务器发生故障,另一台服务器立刻就可以接替工作。

由于静态服务发现仍然依赖人工配置,并且易受配置错误的影响,所以在实际生产环境中很少使用。而动态服务发现又具有很高的实现复杂度和维护成本,在大规模微服务架构中使用不多。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

3.1 Bridge模式网络实现

3.1.1 相关术语定义

在Bridge模式的网络结构中,多个Docker容器共享一个网络栈,并且通过宿主机的网卡进行数据包转发。如下图所示:

如上图所示,宿主机上有两块网卡:

  • eth0: 物理网卡,可以通过DHCP获取IP地址;
  • br0: Linux网桥设备,所有经过网桥的数据包都会先经过该设备。

在Docker容器中运行着多个容器,Docker的每个容器都会获得一个Veth Pair设备,Veth Pair设备代表一个网络接口,可以被添加到网桥br0上,从而连接到宿主机的br0。

为了简化流程,下文仅考虑只有两台机器的场景,如果是多台机器的场景,Docker容器会自动在每台机器上获得一个Veth Pair设备。

3.1.2 数据包发送过程

假设容器A需要跟容器B通信,由于两者处于同一个网络命名空间,所以可以直接通过IP地址来通信。首先,容器A会通过Socket API发送数据包,数据包会通过网卡eth0发送到网桥br0,进而发送给容器B所在的主机上,这台主机上的Veth Pair设备就会接收到这个数据包,然后再发送给容器B。因此,容器A跟容器B之间的通信不会受到限制,因为二者之间仍然处于同一个网络命名空间。

3.1.3 数据包转发过程

当容器A要跟容器B通信时,容器A会将自己跟容器B的Veth Pair设备连接起来,然后就可以像跟主机通信一样,通过IP地址来通信。但是,实际上容器A和容器B之间的通信还需要通过网桥br0才能发送到容器B。

对于一台计算机来说,网桥上可能有多个连接着的Veth Pair设备,所以当数据包来到网桥时,它需要判断目的MAC地址是不是它的一个Veth Pair设备,如果不是,则根据表项将数据包发送到正确的Veth Pair设备。为了避免复杂性,这里仅讨论最简单的情况,即没有VLAN等复杂的配置。

例如,当容器A发送数据包给容器B时,首先查看它的Veth Pair设备的MAC地址,假设是X;此时,数据包会先进入宿主机的br0,但由于宿主机只知道X,并不知道容器B的Veth Pair设备的MAC地址Y。此时,br0会将数据包复制一份,并修改它的源MAC地址为X,目标MAC地址为Y,然后丢弃原数据包。这样,数据包最终会发送到容器B的Veth Pair设备,从而达到双向通信的目的。

注意,在这种情况下,容器A和容器B虽然处于同一个网络命名空间,但是它们仍然是两个不同的网络。也就是说,容器A的进程之间无法直接通信,只能通过网桥。但是,容器A和宿主机上的其他容器之间还是可以通信的,这是因为它们仍然在相同的网络命名空间中。

3.2 Host模式网络实现

3.2.1 相关术语定义

Host模式的网络结构中,多个Docker容器共享一个网络栈,并且直接利用宿主机的网络命名空间。

与Bridge模式类似,在Host模式下,Docker容器之间仍然可以通过Veth Pair设备来通信,不过它使用的是宿主机的网络命名空间,而不是网桥。

与Bridge模式不同的是,容器之间也可以直接通过IP地址通信,并且在宿主机的网络命名空间中暴露容器的端口,使得容器可以被其他进程或者容器访问。

如上图所示,宿主机上有两块网卡:

  • eth0: 物理网卡,可以通过DHCP获取IP地址;
  • lo: loopback网卡,类似于虚拟机中的回环网卡。

在Docker容器中运行着多个容器,Docker的每个容器都会获得一个Veth Pair设备,Veth Pair设备代表一个网络接口,可以被添加到网络命名空间中,从而连接到宿主机的loopback接口。

为了简化流程,下文仅考虑只有两台机器的场景,如果是多台机器的场景,Docker容器会自动在每台机器上获得一个Veth Pair设备。

3.2.2 数据包发送过程

与Bridge模式的网络结构类似,Host模式下的网络栈也需要配置IP地址,然后才能正常通信。对于Host模式下的容器A来说,配置IP地址的方法有以下几种:

  • 使用Docker run命令设置IP地址参数,如--ip=192.168.0.2,注意,此时的IP地址不能和宿主机上的IP地址重复。
  • 配置容器的/etc/hosts文件,可以将容器A的域名映射到指定的IP地址上,从而使得容器A能够通过域名来访问容器B。
  • 配置容器的/etc/resolv.conf文件,可以将DNS服务器指向宿主机的IP地址,从而使得容器A能够解析域名。
  • 在容器A里运行网络代理程序,可以使用如nginx或haproxy这样的Web代理软件,然后将流量导向容器B。

3.2.3 数据包转发过程

由于容器A和容器B共享一个网络命名空间,他们之间的数据包都可以直接通过IP地址通信。容器A可以在宿主机上查看到容器B的IP地址,然后直接向容器B发送数据包。

容器A可以通过Socket API发送数据包,这样就不会受到网桥的干扰。但是,由于容器B的IP地址在网关路由表中并不存在,所以数据包会被丢弃。

为了将数据包正确地转发到容器B,需要在网关路由表中增加一条路由,将容器A的IP地址指向容器B的Veth Pair设备的IP地址。容器A收到的数据包就会通过网关路由表,正确地发送到容器B的Veth Pair设备上。

例如,假设容器A的IP地址是192.168.0.2,容器B的Veth Pair设备的IP地址是192.168.0.3,则需要在网关路由表中新增一条路由:

route add -net 192.168.0.2 netmask 255.255.255.0 gw 192.168.0.3 dev vethBeeFie

这样,所有发往192.168.0.2/24的IPv4数据包都会先经过网关192.168.0.3,然后被转发到Veth Pair设备vethBeeFie上。容器A可以通过Socket API发送数据包,但需要注意,必须发送到192.168.0.3才行,否则数据包会被丢弃。

当然,也可以通过iptables命令或其它网络工具来实现这一功能,但这并不是本文重点。

3.3 None模式网络实现

3.3.1 相关术语定义

None模式的网络结构中,Docker容器之间完全没有网络连接,除非手动配置,否则彼此之间无法通信。

对于None模式的容器A来说,它的网络栈是空白的,即它根本不知道自己属于哪个网络命名空间,也不知道自己的IP地址,所以即便它想要跟容器B通信,也无法实现。

对于None模式的容器B来说,它的IP地址也是空白的,即便它真的有IP地址,也不知道其他容器的IP地址,因此它也无法跟其他容器通信。

在None模式下,容器A和容器B彼此之间不通过任何网络设备或接口来通信,它们只能靠外部进程或容器间的通信。

3.4 container模式网络实现

3.4.1 相关术语定义

container模式的网络结构中,Docker容器之间通过主机上某个容器的网卡直接通信,从而实现通信。

container模式下的容器A依然有Veth Pair设备,和其他的网络模型一样,但由于使用的是宿主机的网络命名空间,所以可以直接与其他容器通信。

假设容器A的名字叫做c1,在宿主机上运行着另一个容器B,容器B的名字叫做c2,并且容器A要跟容器B通信。容器A的配置如下:

version: '3'
services:
  c1:
    image: alpine
    command: tail -f /dev/null
    networks:
      default:
        ipv4_address: 172.17.0.2

  c2:
    image: nginx
    ports:
      - "80:80"
    depends_on:
      - c1
    environment:
      CONTAINER_NAME: c1
    networks:
      default:
        ipv4_address: 172.17.0.3

networks:
  default:
    external: true
    name: my-bridge

在这种情况下,容器A的网卡的IP地址是172.17.0.2,容器B的网卡的IP地址是172.17.0.3。容器A要跟容器B通信,首先需要将Veth Pair设备c1-if和c2-if连接在一起,然后就可以像跟主机通信一样,通过IP地址来通信。

ip link set c1-if master my-bridge # 将Veth Pair设备c1-if添加到my-bridge上
ip addr add 172.17.0.2/16 dev c1-if    # 为c1-if设置IP地址
ip route add default via 172.17.0.1     # 设置默认网关

这样,容器A就可以像跟其他容器通信一样,通过IP地址来通信。

如上图所示,容器A和容器B之间的通信全部由Veth Pair设备完成。

3.5 Overlay模式网络实现

3.5.1 Swarm的相关术语定义

在Swarm模式下,多个Docker容器部署在一个集群上,需要保证容器间的网络连通性。Swarm的网络架构设计包含四层:

  1. 集群网络(Cluster Network):整个集群内的所有节点之间都连接在一个统一的全局的Overlay网络上,这个网络是Swarm管理的,可以看作是独立于具体容器的网络,所有的容器默认都加入到这个网络。

  2. 网格网络(Mesh Network):每个节点之间形成一个虚拟的网络网格,通过独立的IP地址进行通信。

  3. 服务网格(Service Mesh):对于容器服务,部署独立的Sidecar Proxy,Sidecar Proxy按照一定规则控制容器的网络,提高通信效率。

  4. 覆盖网络(Overlay Network):在两个节点之间搭建一个Overlay网络,使得它们可以互相通信。

3.5.2 数据包发送过程

Swarm模式下,容器可以自动加入到Overlay网络,因此不需要手动配置。如下图所示:

容器A、容器B、容器C的IP地址如下:

  • A:10.0.1.2
  • B:10.0.1.3
  • C:10.0.1.4

可以看到,三个容器都加入到了Overlay网络,并且有独立的IP地址。当容器A要跟容器B通信时,它发送的数据包不会经过网关路由器,而是直接发往容器B的IP地址。而容器B接收到数据包后,再转发给容器A。

3.5.3 数据包转发过程

Overlay网络的实现机制主要依赖于IP路由表和隧道技术,容器A要跟容器B通信,它首先会根据自己的IP地址判断应该发送给哪个容器。由于整个Overlay网络是全局共享的,所以路由表里面并没有记录某个容器的IP地址,所以需要借助其他手段进行数据包的转发。

一种比较常用的手段是IPIP隧道技术,即在两个节点之间建立一个IP tunnel,然后容器A发送的数据包会被封装成IP tunnel,通过tunnel传输到容器B。这样,容器B就能接受到这个数据包,进而转发给容器A。

另外,Swarm模式下,部署了Sidecar Proxy,其职责就是拦截发送到某些特殊IP地址的数据包,然后修改数据包的头部信息,使得它们能够正确地被路由到正确的目标。

3.6 Summary

本文总结了容器网络和服务发现两种技术的相关技术要点,分析了常用的网络模式、网络模型和数据包传输过程。对于Bridge模式、Host模式、None模式、container模式、Overlay模式的网络结构以及数据包处理过程进行了详细说明,希望能够帮助读者理解容器网络与服务发现的基本原理,从而更好地应用到日常的开发和运维工作中。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到