Docker学习与实践 - 网络篇
概述
通常来说,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包,另外,如果不同子网之间要进行通信,还需要路由机制。Docker中的网络接口默认都是虚拟接口,虚拟接口的优势之一是转发效率较高。
Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,通俗地讲,发送接口的发送缓存数据包被直接复制到接收接口的接收缓存中,对于本地系统和容器内系统来说,就像是一个正常的以太网卡,但它不需要真正与外部网络设备通信,因此速度快,转发效率高。
Docker的网络实现其实就是利用了该技术,即利用了Linux上的网络命名空间和虚拟网络设备实现的,特别是veth pair
,它在宿主机和容器内分别创建虚拟接口,并让它们彼此连通。
Docker网络实现原理
启动Docker时会在宿主机上创建一个名为docker0
的虚拟网桥,Docker启动一个容器时会根据docker0
的网段划分容器的虚拟子网和私有IP,容器内的虚拟接口为eth0
,各容器的网关则是docker0
的IP。
Docker创建一个容器的时候,会执行如下操作:
- 创建一对虚拟接口,即
veth pair
,分别放到宿主机和容器中; - 主机一端桥接到默认的
docker0
或指定网桥上,具有一个唯一的名字,如veth0ac844e
; - 另一端放到新容器中,并修改名字为
eth0
,该接口只在容器的命名空间可见; - 从网桥可用地址段中获取一个空闲地址分配给容器的
eth0
,并配置默认路由到桥接网卡veth0ac844e
。
这样,该容器就可以使用eth0
虚拟网卡来连接其它容器和网络了。当该容器结束后,Docker会清空容器,容器内的eth0
会随网络命名空间一起被清除,veth0ac844e
接口也被自动从docker0
卸载。
|
|
Docker容器网络模型
网络模型基本组件
在容器网络模型中,通常会包括容器(Container)、沙箱(Sandbox)、端点(Endpoint)、网络(Network)等组件,其中,网络会涉及桥接网(Bridge Network)或重叠网(Overlay Network)。
容器网络模型示意图如下图所示:
- 容器(Container): 容器能够作为任意一个或多个网络的一部分,能够同时对接桥接网和重叠网网络。
- 沙箱(Sandbox): 包含容器网络堆栈配置信息,是一个隔离的环境,可能包含多网络的多个端点。
- 端点(Endpoint): 连接沙箱和网络的接口,绑定到特定的网络,比如之前提到的
veth pair
。 - 网络(Network): 使得一组端点之间能够相互直接交流的,实现可以是Linux网桥或重叠网。
容器网络模型优势
首先,与Docker Links进行简单对比,Docker Links允许容器之间互相发现,并使用容器名作为别名进行互相之间的通信,比DNS或服务发现更容易使用,且不用关心端口映射,但Docker Links有一些限制,比如:
- 只能在同一宿主机内使用,不能跨主机
- 重新创建容器会移除之前的链接(Links)
- 被链接的容器必须是一个已经启动的容器
因此,在Docker 1.9版本之后,官方推荐使用Docker网络功能代替Docker Links。Docker容器网络模型的主要优势在于:
- 在某个特定网络下的所有容器能自由地相互通信
- 多个网络有助于分散容器之间的流量传输
- 多个端点允许一个容器加入到多个网络中
- 能够支持同主机和跨主机的容器间通信
桥接网络与重叠网络
桥接网络(Bridge Network),在同一宿主机上的多个容器可以连接到同一桥接网络来实现彼此网络通信,之前提到了Docker默认的虚拟网桥为docker0
,当然也可以创建自定义的网络生成新的虚拟网桥,而将容器连接到的该虚拟网桥上形成的网络称为桥接网络。在运行容器时可指定--net=bridge
使用桥接模式,将容器桥接到宿主机的虚拟网桥上,从虚拟网桥的空闲网段中划分出一个子网私有IP分配给该容器。
重叠网络(Overly Network,也有的译成叠加或覆盖网络),在物理网络之上,通过软件定义网络(Software Defined Network, SDN) 创建虚拟网络抽象,使得应用之间在逻辑上彼此隔离,但共享相同的底层物理网络,同时简化网络管理。
Overlay networking is a method of using software to create layers of network abstraction that can be used to run multiple separate, discrete virtualized network layers on top of the physical network, often providing new applications or security benefits.
更通俗地讲,重叠网是一种运行于一个或多个已存在的网络之上的虚拟网络,即在IPv4底层网络的基础上通过节点之间单播机制将主机两两相连,形成一个虚拟、独立的具有星形和环形拓扑的虚拟网络,就好像在原有的网络上叠加了一层新的网络,以提供特定的附加功能。
重叠网技术的基本思路是在互联网承载层和应用层之间增加一个中间层,结合IP承载网技术,为上层业务和应用提供有针对性的服务,对已有的应用和业务进行适当控制,是对现有互联网体系架构的系统性修补。在软件定义网络领域中,重叠网络的实现大多采用隧道(Tunneling)技术实现,它可以基于现行的IP网络进行叠加部署,消除传统二层网络的限制,如VXLAN、NVGRE等,都是基于隧道原理实现网络通信,利用叠加在三层网络之上的虚拟网络传递二层数据包,实现了可以跨越三层物理网络进行通信的二层逻辑网络,突破了传统二层网络中存在的物理位置受限、VLAN数量有限等障碍,大幅度降低管理成本和运营风险。
在Docker 1.9之后的版本中,Docker官方集成了重叠网络的特性支持,用户可以在Swarm中使用它或者将其作为Compose工具。通过创建虚拟网络并将其连接到容器上,可实现多个主机上容器相互通信,并且能够实现不同的应用程序或者应用程序不同部分的相互隔离。而在之前的版本中,要实现跨主机的容器间网络通信,需要借助第三方网络插件的实现,可以在后面的章节中了解更多。
Docker四种网络方式
可以在docker run
时通过--net
参数来指定容器的网络配置,有4种可选值:bridge
, host
, container:NAME_or_ID
, none
。
bridge模式
使用--net=bridge
指定,为默认模式,连接到默认的网桥docker0
,此模式会为每个容器分配一个独立的网络命名空间,使用该设置可以看到在容器中创建了eth0
。
详细举个例子,假如需要创建自定义的网络,命名为web
,系统会为其分配一个以br-
开头的网桥名,如br-2e9f4f21f001
,当指定--net=web
时,即连接到对应的网桥上。
|
|
host模式
使用--net=host
指定,Docker使用的网络和宿主机的一样,该方式创建出来的容器,可以看到主机上所有的网络设备,容器中对这些设备有全部的访问权限。
容器进程可以与主机其它root进程一样,能打开低范围的端口,可以访问本地网络服务,如 D-Bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。
因此,使用这个选项的时候要非常小心,在非隔离的环境中使用host模式
是不安全的,如果进一步的使用--privileged=true
,容器可以直接配置主机的网络堆栈。
container模式
使用--net=container:CONTAINER_ID/CONTAINER_NAME
指定,多个容器使用共同的网络,即两者的网络完全相同。
将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享IP地址和端口等网络资源,两者进程可以直接通过 环回接口 通信。
none模式
使用--net=none
指定,这种模式下,将新容器放到隔离的网络栈中,但是不进行网络配置,实际上 nova-docker 用的就是这种方式,这种方式将网络配置的责任完全交给用户,可以实现更加灵活复杂的网络。
Docker网络端口映射
通常来说,容器是用来服务的,这就需要让外部网络能够访问容器,默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。虽然容器本身是有IP地址和端口,但非本地主机是访问不到的,另外,这些IP在容器每次启动时都可能会改变。
Docker给容器内部服务的访问提供了一个简单而可靠的方法,即通过端口绑定主机系统的接口,允许非本地客户端访问容器内部运行的服务,映射容器端口到宿主机的原理是通过在本地的iptables
的NAT表中添加相应的规则实现的,可以通过命令sudo iptables -t nat -nL
查看,具体可以参阅 端口映射实现 了解更多细节。
自动映射端口
使用-P
参数(大写),Docker会自动绑定所有对外提供服务的容器端口,映射的主机端口将会从没有使用的端口池中自动选择。
若指定--expose
选项,表示指定需要对外提供服务的端口。
指定映射端口
使用-p
参数(小写),可以指定要映射的端口,但需要注意的是,在端口映射时应避免主机端口冲突,比如主机的8080端口已经被占用了,那么再映射到该端口就会冲突,即在一个指定端口上只可以绑定一个容器。支持的格式有:
[HOST_PORT:]CONTAINER_PORT
映射所有接口地址,可以指定单组端口映射,比如-p 5678:8082
,也可以指定端口映射范围,比如-p 5678-5679:8082-8083
,主机的端口范围和容器的端口范围必须有效。123$ docker run -d -p 8082 --name review_0 registry:5000/review$ docker run -d -p 8082:8082 --name review_1 registry:5000/review$ docker run -d -p 8182-8183:8082-8083 --name review_2 registry:5000/review此时,默认会绑定本地所有接口上的所有地址,即监听所有网络接口。另外,
-p
可以多次使用来绑定多个端口:1$ docker run -d -p 8382:8082 -p 8383:8083 --name review registry:5000/reviewIP:HOST_PORT:CONTAINER_PORT
映射到指定地址的指定端口,比如这里指定映射使用一个特定地址为localhost地址127.0.0.11$ docker run -d -p 127.0.0.1:8282:8082 --name review_3 registry:5000/reviewIP::CONTAINER_PORT
映射到指定地址的任意端口,比如这里绑定localhost的任意端口到容器的8082端口,本地主机会自动分配一个端口。1$ docker run -d -p 127.0.0.1::8082 --name rewew_4 registry:5000/review还可以使用
udp
标记来指定udp
端口:1$ docker run -d -p 127.0.0.1::8082/udp --name review_5 registry:5000/review
查看端口配置
可以通过docker ps
、docker inspect <CONTAINER>
或docker port <CONTAINER> [PORT]
查看具体的绑定端口和IP等信息。
其中,映射的0.0.0.0
表示将接受主机来自所有接口的流量,同时,可通过上节中提到的参数指定允许访问容器的主机上的IP、端口等,以制定更严格的规则。如果希望永久绑定到某个固定的IP地址,可以在Docker配置文件/etc/default/docker
中指定DOCKER_OPTS="--ip=IP_ADDRESS"
,之后重启Docker服务即可生效。
Docker软件定义网络
什么是软件定义网络?
软件定义网络(Software-Defined Networking, SDN),是由 Emulex 提出的一种新型网络创新架构,其核心技术 OpenFlow 协议通过将网络设备控制平面与数据平面分离开来,从而实现了网络流量的灵活控制,为核心网络及应用的创新提供了良好的平台。
The physical separation of the network control plane from the forwarding plane, and where a control plane controls several devices.
SDN is an emerging architecture that is dynamic, manageable, cost-effective, and adaptable, making it ideal for the high-bandwidth, dynamic nature of today’s applications.
由于传统的网络设备(交换机、路由器)的固件是由设备制造商锁定和控制,而SDN将网络控制与物理网络拓扑分离,创建虚拟网络抽象,从而摆脱硬件对网络架构的限制,并且可对网络架构进行修改。更通俗地讲,网络管理者不直接操作底层的设备,而是通过编程的方式,通过控制器的程序去操作交换机等设备,比如设计转发和路由规则、设计新的网络协议、提供各种各样的服务、实现多个安全方案等,这样的好处在于,可以极大地增强控制层的灵活性。
在DOCKER 1.9之后支持了软件定义网络,增强了网络特性,允许Docker引擎创建虚拟网络,这样,具备网络功能的容器可跨越多个主机进行通信,并且容器间可无缝交流有助于实现真正的分布式应用,另外,开发者也可以更加灵活地搭建类产品环境的测试环境了。
Docker网基本络命令
Docker的network
命令主要包含create
, connect
, disconnect
, inspect
, ls
, prune
以及rm
,可以通过输入帮助命令查看并了解更多。
create
用法:docker network create [OPTIONS] NETWORK
,创建一个新的网络,可配置一些可选参数:123456789101112--attachable Enable manual container attachment--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])-d, --driver string Driver to manage the Network (default "bridge")--gateway stringSlice IPv4 or IPv6 Gateway for the master subnet--internal Restrict external access to the network--ip-range stringSlice Allocate container ip from a sub-range--ipam-driver string IP Address Management Driver (default "default")--ipam-opt map Set IPAM driver specific options (default map[])--ipv6 Enable IPv6 networking--label list Set metadata on a network (default [])-o, --opt map Set driver specific options (default map[])--subnet stringSlice Subnet in CIDR format that represents a network segment在创建一个网络时,不指定
--driver
情况下默认会使用网络驱动bridge
,同时创建一个br-
开头的虚拟网桥,可以在『Docker四种网络方式』章节的『bridge模式』部分中找到相关命令查看详细信息。另外,驱动host
和null
都只能允许管理唯一一个实例网络.connect
用法:docker network connect [OPTIONS] NETWORK CONTAINER
,连接容器到某个网络中,可选参数:12345--alias stringSlice Add network-scoped alias for the container--ip string IP Address--ip6 string IPv6 Address--link list Add link to another container (default [])--link-local-ip stringSlice Add a link-local address for the containerdisconnect
用法:docker network disconnect [OPTIONS] NETWORK CONTAINER
,断开容器到网络的连接,可选参数:1-f, --force Force the container to disconnect from a networkinspect
用法:docker network inspect [OPTIONS] NETWORK [NETWORK...]
,显示在某个网络上的详细信息,可选参数:1-f, --format string Format the output using the given Go templatels
用法:docker network ls [OPTIONS]
,列出当前所有的网络,可选参数:1234-f, --filter filter Provide filter values (e.g. 'driver=bridge')--format string Pretty-print networks using a Go template--no-trunc Do not truncate the output-q, --quiet Only display network IDsprune
用法:docker network prune [OPTIONS]
,删除所有当前未使用的网络,可选参数:1-f, --force Do not prompt for confirmationrm
用法:docker network rm NETWORK [NETWORK...]
,删除指定的一个或多个网络。
同主机的容器网络通信
通过学习了Docker的网络命令,那接下来就练习一下,在同一主机上准备创建两个网络,分别为web
和app
,然后分别在其中创建容器,实现web
网络下的容器与app
网络下的容器相互通信,从而可通过创建虚拟网络模拟出类似产品环境的网络情况。若之前已创建了同名的网络,可根据情况变更网络名称或删除。
创建网络web
并运行容器
创建网络
web
并查看当前所有网络12$ docker network create web$ docker network ls在网络
web
下创建名为web_container
的容器1$ docker run -itd --net=web --name web_container busybox查看网络
web
下的所有容器1$ docker network inspect web
创建网络app
并运行容器
创建网络
app
并查看当前所有网络12$ docker network create app$ docker network ls在网络
app
下创建名为app_container
的容器1$ docker run -itd --name app_container --net=app busybox查看网络
app
下的所有容器1$ docker network inspect app
连接容器到网络并测试
通过命令实现容器
web_container
连接到网络app
1$ docker network connect app web_container这样,容器
web_container
就可以通过app_container.app
连接到容器app_container
。在容器
web_container
中Ping一下app_container.app
12345678$ docker exec -it web_container sh # 进入容器$ ping app_container.app # 在容器内执行,注意返回的IP不应是127.0.x.xPING app_container.app (172.19.0.2): 56 data bytes64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.434 ms...$ exit # 退出容器再次查看网络
web
和app
下的所有容器,特别注意有什么变化?12$ docker network inspect web$ docker network inspect app
跨主机的容器网络通信
在Docker 1.9版本之前,要实现跨主机的容器间网络通信,需要借助第三方网络插件的支持,网络插件在很大程度上降低不同类型主机上容器通信的难度,扩展由Docker提供的核心网络功能。
而在Docker 1.9之后的版本中,Docker官方集成了重叠网络的特性,即支持创建虚拟网络并将其连接到容器上,可实现多个主机上容器相互通信,并且实现不同的应用程序或者应用程序不同部分能够相互隔离,互联子系统设计为可插拔式,兼容VXLAN或者IPVLAN等技术。
目前,Docker容器实现跨主机通信主要通过以下方式:
- 自带重叠网络(Overlay Network) 组件,结合 Docker Swarm 实现,主要原理也是通过Key-Value存储服务进行调用。
- 第三方插件,如 Weave, Calico, Contiv Netplugin, Cisco, VMware, MidoNet, pipework, flannel 等。
而这里将重点介绍自带Overlay Network组件和Docker Swarm的实现方式,首先启动一台虚拟机模拟Node1,将主机模拟为Node2。
初始化 Docker Swarm
在Node1上初始化并创建Docker Swarm,作为Manager。
12345678910$ docker swarm init --advertise-addr 192.168.56.102Swarm initialized: current node (s08ktyv5yij5v7tu66t1ll2fi) is now a manager.To add a worker to this swarm, run the following command:docker swarm join \--token SWMTKN-1-68zbx1wtxgklsj36y3yjq90h6jcx2hewloku685fkvd44m9hw7-acgn20k3ary37fbd2mgtdf9t5 \192.168.56.102:2377To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.在Node2上运行如下命令加入到Swarm中,作为Worker。
1234$ docker swarm join \> --token SWMTKN-1-68zbx1wtxgklsj36y3yjq90h6jcx2hewloku685fkvd44m9hw7-acgn20k3ary37fbd2mgtdf9t5 \> 192.168.56.102:2377This node joined a swarm as a worker.也可以通过命令
docker swarm join-token worker
再次查看其token和IP。在Node1上查看当前所有Node信息
1234$ docker node lsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS3r8p73yl8rkxkyiocnlxzc4wy moby Ready Actives08ktyv5yij5v7tu66t1ll2fi * localhost.localdomain Ready Active Leader
创建 Overlay Network
在Node1上创建Overlay网络命名为
net_overlay
1234567891011$ docker network create -d=overlay net_overlayn9n1no3w0uku2lgn5jo5czc0l$ docker network lsNETWORK ID NAME DRIVER SCOPE694d3a222895 bridge bridge local64f31cf05fee docker_gwbridge bridge local22b5291ac1fb host host localyhb42l4f7a6q ingress overlay swarmn9n1no3w0uku net_overlay overlay swarmb76cb60af763 none null local这样,所有在Swarm中的主机都可以访问该网络了,除了
docker0
默认网桥外,这里多出了一个docker_gwbridge
的网桥,docker0
在跨多主机容器网络中并没有被用到,而是docker_gwbridge
替代了docker0
用来实现Overlay网络中容器间的通信以及容器到外部的通信,其职能就和单机容器网络中docker0
一样。查看
net_overlay
网络的配置信息12345678910111213141516171819$ docker network inspect net_overlay[{"Name": "net_overlay","Id": "n9n1no3w0uku2lgn5jo5czc0l","Scope": "swarm","Driver": "overlay","EnableIPv6": false,"IPAM": {"Driver": "default","Options": null,"Config": [{"Subnet": "10.0.0.0/24","Gateway": "10.0.0.1"}]},...
启动服务并测试连接
创建服务并启动容器
12345$ docker service create --name hello --network net_overlay --replicas 2 ubuntu sleep infinityj56ia0rkjtc39eds80rt3yh4v$ docker service lsj56ia0rkjtc3 hello replicated 1/2 ubuntu:latest这里,创建了一个名为
hello
的服务,并绑定到net_overlay
网络上,同时创建两个容器副本以sleep infinity
的命令运行,该命令使得容器不会立即退出,两个容器分别会运行在两个节点为上。查看容器运行状态
123$ docker service ps hello4fla3n6yogkk hello.1 ubuntu:latest localhost.localdomain Runningp4adrd04a94y hello.2 ubuntu:latest moby Running
然后使用docker ps
命令分别在Node1和Node2上查看容器信息获得容器ID,再通过inspect
命令分别查看两个节点上的容器IP地址,使用exec
命令登录到其中任意一个节点的容器内,使用ping
或traceroute
命令测试是否能连接到另一个节点上的容器,如果没有ping
命令可以执行命令apt-get update; apt-get install iputils-ping -y
安装即可。
小结
主要介绍了Docker网络相关的内容,包括Docker的网络实现原理、容器网络模型组件与优势、桥接网络与重叠网络的概念、Docker的四种网络方式、Docker网络端口映射、软件定义网络概念等,同时还练习了Docker网络基本命令,配置了同主机的容器虚拟网络,以及搭建了跨主机的容器虚拟网络。在整体和细节上都对Docker网络有了一个更清晰的认识,同时也对Docker网络相关命令更加熟练,能够配置同主机上的容器网络,并且也能够搭建跨主机的容器网络环境。
References
- Docker networking
- Docker container networking
- Docker 网络实现
- What is Overlay Networking?
- Work with network commands
- Docker学习笔记:Docker 网络配置
- Software-Defined Networking Definition
- 理解Docker跨多主机容器网络
- Get started with multi-host networking
- Demystifying Docker overlay networking
- Docker 1.9 Overlay Network实现跨主机网络互通
- Run Docker Engine in swarm mode