关于docker容器网络的一些理解

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.


这里写图片描述
打开微信扫一扫,关注微信公众号【数据与算法联盟】

转载请注明出处: http://blog.csdn.net/gamer_gyt
博主微博: http://weibo.com/234654758
Github: https://github.com/thinkgamer


参考资料

1:容器网络那些事儿
2:使用 Docker 容器网络
3:Docker 1.9的新网络特性,以及Overlay详解(1)
4:容器互联
5:docker的网络-Container network interface(CNI)与Container network model(CNM)


写在前边的话

      突然发现好久没有更新博客了,像我这种频繁发表博客的人竟然也会放慢了更新的速度,其实不是说自己不去写,不去更新,只是不愿意去将就,去发表一些让别人看了没有多大帮助的文章,作为2017年的开篇博客,我想和你们一起学习下docker容器网络的知识,首先声明,以下内容大部分都是来源网络,按照我对docker网络的理解,整理的一篇文章,一起学习。


docker容器网络概述

1:默认网络

      在默认情况下会看到三个网络,它们是Docker Deamon进程创建的。它们实际上分别对应了Docker过去的三种『网络模式』,可以使用docker network ls来查看

master@ubuntu:~$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
18d934794c74        bridge              bridge              local
f7a7b763f013        host                host                local
697354257ae3        none                null                local

      这 3 个网络包含在 Docker 实现中。运行一个容器时,可以使用 the –net标志指定您希望在哪个网络上运行该容器。您仍然可以使用这 3 个网络。

  • bridge 网络表示所有 Docker 安装中都存在的 docker0 网络。除非使用 docker run –net=选项另行指定,否则 Docker 守护进程默认情况下会将容器连接到此网络。在主机上使用 ifconfig命令,可以看到此网桥是主机的网络堆栈的一部分。
  • none 网络在一个特定于容器的网络堆栈上添加了一个容器。该容器缺少网络接口。
  • host 网络在主机网络堆栈上添加一个容器。您可以发现,容器中的网络配置与主机相同。

2:自定义网络

      当然你也可以自定义网络来更好的隔离容器,Docker 提供了一些默认网络驱动程序来创建这些网络。您可以创建一个新 bridge 网络或覆盖一个网络。也可以创建一个网络插件或远程网络并写入您自己的规范中。您可以创建多个网络。可以将容器添加到多个网络。容器仅能在网络内通信,不能跨网络进行通信。一个连接到两个网络的容器可与每个网络中的成员容器进行通信。当一个容器连接到多个网络时,外部连接通过第一个(按词典顺序)非内部网络提供。

(1):docker network 命令

执行 sudo docker network –help

master@ubuntu:~$ sudo docker network --help

Usage:  docker network COMMAND

Manage Docker networks

Options:
      --help   Print usage

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  rm          Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.

(2):创建test-network网络

执行命令: sudo docker network create test-network
查看: sudo docker network ls

master@ubuntu:~$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
18d934794c74        bridge              bridge              local
f7a7b763f013        host                host                local
697354257ae3        none                null                local
c4f6d347c8b4        test-network        bridge              local

查看自己创建的网络的信息

master@ubuntu:~$ sudo docker network inspect test-network
[
    {
        "Name": "test-network",
        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

另外,还可以采用其他一些选项,比如 –subnet、–gateway和 –ip-range。

(3):启动容器连接到test-network

sudo docker run -itd –name=test –net=test-network lt:1.0 /bin/bash

master@ubuntu:~$ sudo docker run -itd --name=test  --net=test-network lt:1.0 /bin/bash
09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315

再次查看信息,可以看到挂载的容器

master@ubuntu:~$ sudo docker network inspect test-network
[
    {
        "Name": "test-network",
        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": {
                "Name": "test",
                "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

当然也可以动态的将容器挂载到某个网络上

master@ubuntu:~$ sudo docker run -itd --name=test1 lt:1.0 /bin/bash
b2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334
master@ubuntu:~$ sudo docker network connect test-network test1
master@ubuntu:~$ sudo docker network inspect test-network
[
    {
        "Name": "test-network",
        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": {
                "Name": "test",
                "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "b2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334": {
                "Name": "test1",
                "EndpointID": "5d18b876b62312698d4ce253e33b52552c9a3b1237bed90ec565d4cbd9f5710d",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

“石器时代”的容器网络模型

      目前对于刚起步接触docker容器的筒子们(当然也包括我),大部分使用网络的方式应该是这样的,把需要暴漏的端口做端口映射(docker run -itd -p 81:80 …),例如一个主机内有很多Apache容器,每一个Apache要往外抛80的端口,那我怎么办?我需要针对第一个容器和主机80端口做映射,第二个和主机81端口做映射,依此类推,到最后发现非常混乱,没办法管理。这样的容器网络模型对于企业来说是基本没办法被采用。

这里写图片描述

      这是石器时代网络模型,它是Docker1.9之前的容器网络,实现方式是只针对单台主机进行IPAM管理,所有主机上的容器都会连接到主机内部的一个Linux Bridge,叫Docker0,主机的IP它默认会分配172.17网段中的一个IP,因为有Docker0,所以在一个主机上的容器可以实现互联互通。但是因为IP分配的范围是基于单主机的,所以你会发现在其他主机上,也会出现完全相同的IP地址。很明显,这两个地址肯定没办法直接通信。为了解决这个问题,在石器时代我们会用端口映射,实际上就是NAT的方法。比如说我有一个应用,它有Web和Mysql,分别在不同的主机上,Web需要去访问Mysql,我们会把这个Mysql的3306端口映射到主机上的3306这个端口,然后这个服务实际上是去访问主机IP 10.10.10.3 的3306端口,这是过去的石器时代的一个做法。

      总结一下它的典型技术特征:基于单主机的IPAM;主机之内容器通讯实际上通过一个docker0的Linux Bridge;如果服务想要暴露到外部的话需要做NAT,会导致端口争抢非常严重;当然它有一个好处,对大网IP消耗比较少。


docker容器的单主机通信

      docker容器的单主机通信使用的是容器互联技术,即 –link,映射网络端口不是吧container彼此连接起来的唯一方法。Docker的linking系统允许你吧多个 container连接起来, 让他们彼此交互信息。Docker的linking会创建一种父子级别的关系。 父container可以看到他的子container提供的信息。
创建一个数据库容器:

sudo docker run -d –name db training/postgres

然后创建一个新的 web 容器,并将它连接到 db 容器

sudo docker run -d -P –name web –link db:db training/webapp python app.py

此时,db 容器和 web 容器建立互联关系。
–link 参数的格式为 –link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。


docker容器的跨主机通信

      早期大家的跨主机通信方案主要有以下几种:

  • 容器使用host模式:容器直接使用宿主机的网络,这样天生就可以支持跨主机通信。虽然可以解决跨主机通信问题,但这种方式应用场景很有限,容易出现端口冲突,也无法做到隔离网络环境,一个容器崩溃很可能引起整个宿主机的崩溃。
  • 端口绑定:通过绑定容器端口到宿主机端口,跨主机通信时,使用主机IP+端口的方式访问容器中的服务。显而易见,这种方式仅能支持网络栈的四层及以上的应用,并且容器与宿主机紧耦合,很难灵活的处理,可扩展性不佳。
  • docker外定制容器网络:在容器通过docker创建完成后,然后再通过修改容器的网络命名空间来定义容器网络。典型的就是很久以前的pipework,容器以none模式创建,pipework通过进入容器的网络命名空间为容器重新配置网络,这样容器网络可以是静态IP、vxlan网络等各种方式,非常灵活,容器启动的一段时间内会没有IP,明显无法在大规模场景下使用,只能在实验室中测试使用。
  • 第三方SDN定义容器网络:使用Open vSwitch或Flannel等第三方SDN工具,为容器构建可以跨主机通信的网络环境。这些方案一般要求各个主机上的docker0网桥的cidr不同,以避免出现IP冲突的问题,限制了容器在宿主机上的可获取IP范围。并且在容器需要对集群外提供服务时,需要比较复杂的配置,对部署实施人员的网络技能要求比较高。

      上面这些方案有各种各样的缺陷,同时也因为跨主机通信的迫切需求,docker 1.9版本时,官方提出了基于vxlan的overlay网络实现,原生支持容器的跨主机通信。同时,还支持通过libnetwork的plugin机制扩展各种第三方实现,从而以不同的方式实现跨主机通信。就目前社区比较流行的方案来说,跨主机通信的基本实现方案有以下几种:

  • 基于隧道的overlay网络:按隧道类型来说,不同的公司或者组织有不同的实现方案。docker原生的overlay网络就是基于vxlan隧道实现的。ovn则需要通过geneve或者stt隧道来实现的。flannel最新版本也开始默认基于vxlan实现overlay网络。
  • 基于包封装的overlay网络:基于UDP封装等数据包包装方式,在docker集群上实现跨主机网络。典型实现方案有weave、flannel的早期版本。
  • 基于三层实现SDN网络:基于三层协议和路由,直接在三层上实现跨主机网络,并且通过iptables实现网络的安全隔离。典型的方案为Project Calico。同时对不支持三层路由的环境,Project Calico还提供了基于IPIP封装的跨主机网络实现。

docker容器的CNI模型和CNM模型

      目前围绕着docker的网络,目前有两种比较主流的声音,docker主导的Container network model(CNM)和社区主导的Container network interface(CNI)。

1:CNI

(1) 概述

      Container Networking Interface(CNI)提供了一种linux的应用容器的插件化网络解决方案。最初是由rkt Networking Proposal发展而来。也就是说,CNI本身并不完全针对docker的容器,而是提供一种普适的容器网络解决方案。因此他的模型只涉及两个概念:

      容器(container) : 容器是拥有独立linux网络命名空间的独立单元。比如rkt/docker创建出来的容器。
      这里很关键的是容器需要拥有自己的linux网络命名空间。这也是加入网络的必要条件。

      网络(network): 网络指代了可以相互联系的一组实体。这些实体拥有各自独立唯一的ip。这些实体可以是容器,是物理机,或者其他网络设备(比如路由器)等。

(2) 接口及实现

      CNI的接口设计的非常简洁,只有两个接口ADD/DELETE。

      以 ADD接口为例

      Add container to network

      参数主要包括:

  • Version. CNI版本号
  • Container ID. 这是一个可选的参数,提供容器的id
  • Network namespace path. 容器的命名空间的路径,比如 /proc/[pid]/ns/net。
  • Network configuration. 这是一个json的文档,具体可以参看network-configuration
  • Extra arguments. 其他参数
  • Name of the interface inside the container. 容器内的网卡名

      返回值:

  • IPs assigned to the interface. ipv4或者ipv6地址
  • DNS information. DNS相关信息

2:CNM

      相较于CNI,CNM是docker公司力推的网络模型。其主要模型如下图:

这里写图片描述

Sandbox

      Sandbox包含了一个容器的网络栈。包括了管理容器的网卡,路由表以及DNS设置。一种Sandbox的实现是通过linux的网络命名空间,一个FreeBSD Jail 或者其他类似的概念。一个Sandbox可以包含多个endpoints。

Endpoint

      一个endpoint将Sandbox连接到network上。一个endpoint的实现可以通过veth pair,Open vSwitch internal port 或者其他的方式。一个endpoint只能属于一个network,也只能属于一个sandbox。

Network

      一个network是一组可以相互通信的endpoints组成。一个network的实现可以是linux bridge,vlan或者其他方式。一个网络中可以包含很多个endpoints。

接口

      CNM的接口相较于CNI模型,较为复杂。其提供了remote plugin的方式,进行插件化开发。remote plugin相较与CNI的命令行,更加友好一些,是通过http请求进行的。remote plugin监听一个指定的端口,docker daemon直接通过这个端口与remote plugin进行交互。

鉴于CNM的接口较多,这里就不一一展开解释了。这里主要介绍下在进行docker的操作中,docker daemon是如何同CNM插件繁盛交互。

调用过程

  • Create Network

      这一系列调用发生在使用docker network create的过程中。

      /IpamDriver.RequestPool: 创建subnetpool用于分配IP
      /IpamDriver.RequestAddress: 为gateway获取IP
      /NetworkDriver.CreateNetwork: 创建neutron network和subnet

  • Create Container

      这一系列调用发生在使用docker run,创建一个contain的过程中。当然,也可以通过docker network connect触发。

      /IpamDriver.RequestAddress: 为容器获取IP
      /NetworkDriver.CreateEndpoint: 创建neutron port
      /NetworkDriver.Join: 为容器和port绑定
      /NetworkDriver.ProgramExternalConnectivity:
      /NetworkDriver.EndpointOperInfo

  • Delete Container

      这一系列调用发生在使用docker delete,删除一个contain的过程中。当然,也可以通过docker network disconnect触发。

      /NetworkDriver.RevokeExternalConnectivity
      /NetworkDriver.Leave: 容器和port解绑
      /NetworkDriver.DeleteEndpoint
      /IpamDriver.ReleaseAddress: 删除port并释放IP

  • Delete Network

      这一系列调用发生在使用docker network delete的过程中。

      /NetworkDriver.DeleteNetwork: 删除network
      /IpamDriver.ReleaseAddress: 释放gateway的IP
      /IpamDriver.ReleasePool: 删除subnetpool

3:CNI与CNM的转化

      CNI和CNM并非是完全不可调和的两个模型。二者可以进行转化。比如calico项目就是直接支持两种接口模型。

      从模型中来看,CNI中的container应与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。在CNI中,CNM中的endpoint被隐含在了ADD/DELETE的操作中。CNI接口更加简洁,把更多的工作托管给了容器的管理者和网络的管理者。从这个角度来说,CNI的ADD/DELETE接口其实只是实现了docker network connect和docker network disconnect两个命令。

      kubernetes/contrib项目提供了一种从CNI向CNM转化的过程。其中原理很简单,就是直接通过shell脚本执行了docker network connect和docker network disconnect命令,来实现从CNI到CNM的转化。

相关文章
|
3天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
13 2
|
1天前
|
运维 Cloud Native 虚拟化
一文吃透云原生 Docker 容器,建议收藏!
本文深入解析云原生Docker容器技术,涵盖容器与Docker的概念、优势、架构设计及应用场景等,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
一文吃透云原生 Docker 容器,建议收藏!
|
2天前
|
缓存 监控 开发者
掌握Docker容器化技术:提升开发效率的利器
在现代软件开发中,Docker容器化技术成为提升开发效率和应用部署灵活性的重要工具。本文介绍Docker的基本概念,并分享Dockerfile最佳实践、容器网络配置、环境变量和秘密管理、容器监控与日志管理、Docker Compose以及CI/CD集成等技巧,帮助开发者更高效地利用Docker。
|
3天前
|
监控 持续交付 Docker
Docker 容器化部署在微服务架构中的应用有哪些?
Docker 容器化部署在微服务架构中的应用有哪些?
|
3天前
|
监控 持续交付 Docker
Docker容器化部署在微服务架构中的应用
Docker容器化部署在微服务架构中的应用
|
3天前
|
安全 持续交付 Docker
微服务架构和 Docker 容器化部署的优点是什么?
微服务架构和 Docker 容器化部署的优点是什么?
|
5天前
|
NoSQL Redis Docker
【赵渝强老师】使用Docker Compose管理容器
Docker Compose 通过 YAML 文件管理多个容器,简化复杂系统的部署和管理。本文介绍了 Docker Compose 的基本概念,并通过一个包含 Redis DB 和 Python Web 模块的示例,展示了如何使用 Docker Compose 部署和管理多容器应用。手动部署和 Docker Compose 部署的对比突显了 Docker Compose 在系统复杂度增加时的优势。
|
5天前
|
Docker 容器
【赵渝强老师】Docker的None网络模式
Docker容器在网络方面实现了逻辑隔离,提供了四种网络模式:bridge、container、host和none。其中,none模式下容器具有独立的网络命名空间,但不包含任何网络配置,仅能通过Local Loopback网卡(localhost或127.0.0.1)进行通信。适用于不希望容器接收任何网络流量或运行无需网络连接的特殊服务。
|
5天前
|
Docker 容器
【赵渝强老师】Docker的Host网络模式
Docker容器在网络环境中是隔离的,可通过配置不同网络模式(如bridge、container、host和none)实现容器间或与宿主机的网络通信。其中,host模式使容器与宿主机共享同一网络命名空间,提高性能但牺牲了网络隔离性。
|
5天前
|
Kubernetes Docker 容器
【赵渝强老师】Docker的Container网络模式
Docker容器在网络环境中彼此隔离,但可通过配置不同网络模式实现容器间通信。其中,container模式使容器共享同一网络命名空间,通过localhost或127.0.0.1互相访问,提高传输效率。本文介绍了container模式的特点及具体示例。