侧边栏壁纸
  • 累计撰写 51 篇文章
  • 累计创建 23 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

K8s 网络1

zhanjie.me
2020-11-19 / 0 评论 / 0 点赞 / 3 阅读 / 0 字

1.1 Kubernetes 网络模型

IP-per-Pod 模型

基础原则:每个 Pod 都拥有一个独立的 IP 地址,并假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中。不管这些 Pod 是否运行在同一个 Node 节点上,都要求它们之间直接通过对方的 IP 就可以进行访问。

设计原因:用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。

在 kubernetes 集群中,IP 是以 Pod 为单位进行分配的,在 Pod 内部的所有容器共享一个网络命名空间(即:IP 地址、网络设备、配置等)。因此,为每个 Pod 都设置一个 IP 地址的这种模式被称为 IP-per-Pod 模型。

IP-per-Pod 模型假设 Pod 之间访问时使用的是对方 Pod 的实际地址,所以一个 Pod 内部的应用程序看到的自己的 IP 地址和端口与集群内其他 Pod 看到的是相同的,即:IP 地址和端口在 Pod 内外部都保持一致,不使用 NAT 进行转换。它们都是 Pod 实际分配的 IP 地址(从 docker0 上分配)。

IP-per-Pod 模型的另一层含义是:同一个 Pod 内的不同容器共享同一个网络命名空间,容器之间通过 localhost 来连接对方的端口。这样可以减少 Pod 内容器之间的隔离性,更加简单高效地共享资源、互相通信。

因此,IP-per-Pod 模型是一个简单、兼容性良好的模型。无论是从网络端口分配、域名解析、服务发现,还是负载均衡、应用配置和迁移等方面来看,采用这种模型以后,Pod 都可以被视为一台独立的虚拟机。

kubernetes 集群中对网络的要求如下:

  • 所有容器之间无需 NAT(网络地址转换)就可以直接互相访问通信。
  • 所有 Node 和所有容器之间无需 NAT(网络地址转换)就可以直接互相访问通信。
  • 容器自己看到的 IP 与其它容器看到的是一样的。

1.2 Kubernetes 网络实现

Kubernetes 的网络实现总共可以划分为 4 个层级:

  • 容器和容器之间的网络。
  • Pod 和 Pod 之间的网络。
  • Pod 和 Service 之间的网络。
  • Internet 与 Service 之间的网络。

容器和容器之间的网络

容器和容器之间的网络通信图示如下:

image-qbbwofyu.png

表示的是在一个 Node 上运行着一个 Pod 实例,在 Pod 中运行着容器 1 和容器 2。容器 1 和容器 2 共享相同的网络命名空间,打开的端口不会产生冲突。

通过设置 Docker 的 --net=container,指定新创建的 Docker 容器与已经存在的容器共享相同的网络命名空间,而不是和宿主机共享。即:新创建的 Docker 容器不会创建自己的网卡、配置 IP 等,而是与指定的容器共享 IP、端口范围。这个指定的容器就是 Pause 容器,每次在 Pod 上执行创建容器时,首先都会创建一个 Pause 容器(这个容器的唯一任务就是保留并持有一个网络命名空间 netns),然后再创建其它满足要求的容器,其它容器就会加入到 Pause 容器的网络命名空间中。这些容器之间的关系如下图所示:

image-cbkjpqgx.png


Pod 和 Pod 之间的网络

在 kubernetes 集群中,每个 Pod 都有一个真实的全局 IP 地址,Pod 之间直接通过对方的 IP 地址就可以实现访问。但是我们知道 Pod 可能分布于不同的 Node 上,因此这里的情况也可以分为两类:

  • 同一个 Node 内 Pod 之间的通信。
  • 不同 Node 上 Pod 之间的通信。

同一个 Node 内 Pod 之间的通信

同一个 Node 内 Pod 之间的通信图示如下:

image-pqrddkjk.png

Pod1 和 Pod2 都是通过 Veth 连接到同一个 docker0 网桥上的,Pod1 的 IP1 地址、Pod2 的 IP2 地址都是从 docker0 网段上动态获取的,它们和网桥上的 IP3 地址处于同一个网段。

这中间使用 Linux 虚拟以太网设备,使用由两个(或是多个)虚拟接口组成的 Veth 将不同的网络命名空间连接起来。这些虚拟接口分布在多个网络命名空间(即:多个 Pod 上),Veth 的一端连接到 root 网络命名空间(即:Node 上),另一端连接到 Pod 的网络命名空间上。

在 Pod1 和 Pod2 的 Linux 协议栈中,默认路由为 docker0 的地址,所以所有非本地地址的网络数据,会被默认发送到 docker0 网桥上,由 docker0 网桥进行中转。

就是通过 docker0 网桥以及相同的地址段,从而实现了同一个 Node 内不同 Pod 之间的通信。

不同 Node 上 Pod 之间的通信

不同 Node 上 Pod 之间的通信图示如下:

image-pkpyvjap.png

Pod1 的 IP 地址是 IP1,Pod2 的 IP 地址是 IP2,Node 1 的 IP 地址为是 IP3,Node2 的 IP 地址是 IP4。想要实现 Pod 1 访问 Pod2,就将数据通过 Node1 的 eth0 发送,通过寻址找到 Node2 的 ech0 接收。所以整个过程就是从:IP1 -> IP3 -> IP4 -> IP2。

通过前面的介绍我们知道,Pod 的地址和 docker0 处于相同网段,但是 docker0 网段与所在 Node 节点的网卡是两个完全不同的 IP 网段。不同 Node 节点之间的通信只能通过物理网卡进行实现,因此肯定要实现通过主机进行 IP 寻址。

kubernetes 集群中的 etcd 数据库会记录所有正在运行的 Pod 的 IP 地址信息,方便 Pod 之间进行通信时能够及时获取到对方 Pod 的地址。这就要求 Pod 的 IP 地址一定不能有冲突。

因此,想要在不同 Node 上进行 Pod 之间的通信必须满足以下两点要求:

  • 集群统一为 Pod 进行 IP 地址分配,保证不冲突。
  • 将 Pod 的 IP 与 Node 的 IP 进行关联,使得即使不在相同节点 Pod 之间也可以互相访问。

对于 kubernetes 本身并没有实现网络规划的具体逻辑,只是制定了一套 CNI(Container Network Interface)接口规范,然后开放给社区来实现各种网络插件。大家可以根据自己的生产环境特点,使用不同的开源网络插件完成对于网络的部署。


2.1 CNI 网络模型简介

CNI(Container Network Interface) 网络模型是由 CoreOS 公司提出的一种容器网络规范,这个规范被大量与云原生相关的项目所采纳。当然,kubernetes 也是使用的这个模型。

CNI 网络模型主要用于创建一个框架,当创建或销毁容器时动态的配置或删除网络资源。换句话说,CNI 网络模型只专注于解决容器网络连接和容器销毁时的资源释放问题。而具体的网络模式就交给其它各种插件来实现。

运行环境和各种网络插件通过 CNI 进行连接的模型如下所示:

image-vtlorxtx.png

CNI 模型中只涉及两个概念:

  • Container:容器。拥有独立 Linux 网络命名空间的环境。
  • Network:网络。拥有独立且唯一的 IP 地址、彼此间能够互相连接的一组实体。

CNI 网络模型中对容器网络的设置和操作都是通过插件(Plugin)来实现。分为两种类型的插件:

  • CNI Plugin:为容器配置网络资源。
  • IPAM Plugin:IPAM 是 IP Address Management 的缩写,也就是 IP 地址管理的插件。主要负责对容器的 IP 地址进行分配和管理。IPAM Plugin 主要用于协助 CNI Plugin。

CNI 网络模型的主要作用在如下三点:

  • kubernetes 集群使用 CNI Plugin 来组建容器网络。
  • 当容器创建时,kubernetes 调用 CNI Plugin 的接口生成网络配置;以及容器删除时,同时也删除相关的网络配置。
  • CNI Plugin 生成虚拟 NIC,将其挂载在主机网络上,并和 Pod 的命名空间(namespace)关联。

当 kubernetes 创建 Pod 后,CNI 网络模型提供网络的过程主要分为以下三个步骤:

  • kubelet runtime 创建 network namespace。
  • kubelet 调用 CNI Plugin,指定网络类型。网络类型将决定哪一个 CNI Plugin 将被使用。
  • CNI Plugin 创建 veth pair,检查 IPAM 类型和数据,触发 IPAM Plugin,获取空闲的 IP 地址并将地址分配给容器的网络接口。

2.2 CNI Plugin

CNI Plugin 的接口有 4 个:ADD(添加)、DELETE(删除)、CHECK(检查)、VERSION(版本)。

ADD(添加)

主要作用是当创建容器时,将容器添加到某个网络。当容器运行时创建容器时,首先创建容器的网络命名空间,然后调用 CNI Plugin 进行网络配置,最后再启动容器中的进程。

“添加”接口的参数有:

  • Version:CNI 版本号。
  • Container ID:容器 ID。
  • Network namespace path:容器的网络命名空间路径。
  • Network configuration:网络配置文档,一般是 json 格式。
  • Extra arguments:其它参数。
  • Name of the interface inside the container:容器内网卡名。

“添加”接口返回的信息有:

  • Interfaces list:网卡列表。
  • IPs assigned to the interface:IPv4/IPv6 的网关地址、路由信息等。
  • DNS information:DNS 相关信息。

DELETE(删除)

主要作用是当销毁容器时,将容器从某个网络中删除。

“删除”接口的参数和“添加”接口的参数完全一致,这里不再重复。

CHECK(检查)

主要作用是检查容器网络是否正确设置。

“检查”接口的参数除了没有 Version 号以外,其它的参数和“添加”接口的参数完全一致,这里不再重复。

VERSION(版本)

主要作用是查询网络插件支持的 CNI 版本号。

无参数。返回值是网络插件支持的版本号。

返回结果示例如下:

{
  "cniVersion": "0.4.0", // CNI 的版本号
  "supportedVersions": ["0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"] // 列出这个插件支持的 CNI 版本号
}

传递参数

传递参数的方式有两种:环境变量和标准输入(网络配置参数)。通过环境变量设置可执行程序的路径(CNI_PATH),容器运行时会调用可执行程序,并通过环境变量和标准输入传递参数,CNI Plugin 完成最终的容器网络资源和参数的设置。

环境变量有如下这些参数:

  • CNI_COMMAND:接口方法,可选值为:ADD、DEL 和 VERSION。
  • CNI_CONTAINERID:容器 ID。
  • CNI_NETNS:容器的网络命名空间路径。
  • CNI_IFNAME:待设置的网络接口名称。
  • CNI_ARGS:可以设置其他参数,格式为 key-value 键值对形式。
  • CNI_PATH:可执行程序的路径,可以设置多个路径。

标准输入(网络配置参数)是 json 格式类型,有如下这些参数:

  • cniVersion:CNI 版本号。
  • name:网络名称,在管理域内应该是唯一的。
  • type:CNI Plugin 可执行文件名。
  • args:其他参数。
  • ipMasq:是否需要设置 IP Masquerade(插件支持),适用于主机可作为网关的环境中。
  • ipam:IP 地址管理的相关配置。
  • type:IPAM 可执行文件名。* dns:DNS 服务相关配置。
  • nameservers:名字服务器列表。
  • domain:本地域名。
  • search:按优先级排序的域名优先级查询列表。
  • options:传递给 resolver 的选项列表。

网络插件的类型

在上面一直介绍的都是 CNI Plugin,其实在 kubernetes 集群中还有另外一种网络插件名为 kubenet Plugin,这种插件很具体,就是使用 bridge 和 host-local CNI Plugin 实现一个基本的 cbr0。不过这种类型的网络插件使用的不多,这里就不详细介绍了。

想要使用网络插件,在 kubelet 服务的启动参数上还需要设置如下的参数:

  • network-plugin-dir:kubelet 启动时查找网络插件的目录。
  • network-plugin:如果是 CNI Plugin,则设置为 cni;如果是 kubenet Plugin,则设置为 kubenet。
  • cni-conf-dir:CNI Plugin 的配置文件目录,默认为 /etc/cni/net.d。
  • cni-bin-dir:CNI Plugin 的可执行文件目录,默认为 /opt/cni/bin。

需要注意的是:CNI Plugin 是一个统称,所有符合 CNI 网络模型要求的插件都可以被称为 CNI Plugin。在社区中已经有很多符合 CNI Plugin 要求的开源插件项目,比如:Calico、Flannel、Weave Net 等。


2.3 IPAM Plugin

IPAM Plugin 主要用于管理容器的 IP 地址,为容器分配 IP 地址、网关、路由和 DNS。典型的实现包括 host-local 和 dhcp。

IPAM Plugin 也是通过可执行程序来完成对于 IP 地址的分配。同时也处理传递给 CNI Plugin 的环境变量和通过标准输入传入的网络配置参数。

一般是在 CNI Plugin 运行时自动调用 IPAM Plugin 来完成容器 IP 地址的分配。

当 IPAM Plugin 完成了容器 IP 地址的分配,会通过标准输出返回 json 格式的报文。报文主要分为四个部分:

  • cniVersion:CNI Plugin 的版本号。
  • ips:分配给容器的 IP 地址(包括网关)。
  • routes:路由规则记录。
  • dns:DNS 相关信息。

返回结果示例如下:

{
  "cniVersion": "0.4.0",
  "ips": [
      {
          "version": "<4-or-6>",
          "address": "<ip-and-prefix-in-CIDR>",
          "gateway": "<ip-address-of-the-gateway>",          // 可选
          "interface": <numeric index into 'interfaces' list>
      },
      ...
  ],
  "routes": [                                                // 可选
      {
          "dst": "<ip-and-prefix-in-cidr>",
          "gw": "<ip-of-next-hop>"                           // 可选
      },
      ...
  ],
  "dns": {                                                   // 可选
    "nameservers": <list-of-nameservers>                     // 可选
    "domain": <name-of-local-domain>                         // 可选
    "search": <list-of-additional-search-domains>            // 可选
    "options": <list-of-options>                             // 可选
  }
}

2.4 CNI 测试实例

下面在安装了docker的Ubuntu环境中安装 CNI 插件,进行配置以后使用一个简单的容器进行测试。

$ docker version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.39
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 17:02:59 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.9
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.11.13
  Git commit:       039a7df
  Built:            Wed Sep  4 16:19:38 2019
  OS/Arch:          linux/amd64
  Experimental:     false
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.5 LTS
Release:	16.04
Codename:	xenial

因为 CNI 插件是使用 Go 语言编写的,这里首先安装 Go:

$ sudo add-apt-repository ppa:longsleep/golang-backports
$ sudo apt-get update
$ sudo apt-get install -y golang-go
$ mkdir $HOME/go
$ vim ~/.bashrc
# 向其中添加环境变量和路径
export GOPATH=$HOME/go
$ source .bashrc

下载和编译 CNI Plugin:

$ mkdir $HOME/go/src
$ cd $HOME/go/src
$ git clone https://github.com/containernetworking/plugins.git
$ cd plugins
$ ./build_linux.sh
$ sudo mkdir -p /opt/cni/bin
$ sudo cp ./bin/* /opt/cni/bin

编写 CNI 配置信息,/etc/cni/net.d 是 CNI 默认的网络配置文件目录,如果没有特别指定其它路径,就会默认从这个目录下加载配置文件创建容器网络。

$ sudo mkdir -p /etc/cni/net.d
$ sudo vim /etc/cni/net.d/10-mynet.conf
# 向其中写入如下内容
{
    "cniVersion": "0.2.0",
    "name": "mynet",
    "type": "bridge",
    "bridge": "cni0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.22.0.0/16",
        "routes": [
            { "dst": "0.0.0.0/0" }
        ]
    }
}
$ sudo vim /etc/cni/net.d/99-loopback.conf
# 向其中写入如下内容
{
    "cniVersion": "0.2.0",
    "name": "lo",
    "type": "loopback"
}

运行容器进行测试:

为了让 Docker 使用 CNI 网络,CNI 官方提供了相关脚本,通过该脚本运行容器。在这里通过环境变量设置 CNI 配置文件和执行命令的路径。

$ cd $HOME/go/src
$ git clone https://github.com/containernetworking/cni.git
$ cd cni/scripts
$ export CNI_PATH=/opt/cni/bin/ # 存放 CNI 网络插件可执行文件的文件夹路径
$ export NETCONFPATH=/etc/cni/net.d # 存放 CNI 网络插件配置信息的文件夹路径
$ sudo apt-get install -y jq
$ sudo CNI_PATH=$CNI_PATH ./docker-run.sh --rm busybox:latest ifconfig
eth0      Link encap:Ethernet  HWaddr 72:10:C1:79:4E:EE
          inet addr:10.22.0.2  Bcast:10.22.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:618 (618.0 B)  TX bytes:56 (56.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

可以看到,docker 容器的网络地址是配置的 CNI 网络 10.22.0.0/16。

0

评论区