站内链接:
Install Mac 下载 docker for mac, 安装即可
Ubuntu 安装: sudo apt-get install -susdo docker.io
添加用户: sudo usermod -a -G docker <username>
启动服务: sudo service docker start
虚拟网络 多 IP 一个网卡上设置多个 IP
1 2 3 4 ifconfig eth0:1 192.168.56.2 netmask 255.255.255.0 ip address eth0
tap 和 tun tap/tun
虚拟网卡或者虚拟网络设备是 linux2.4 之后实现的虚拟网络设备, 该虚拟网卡完全由软件来实现, 功能同硬件实现没有区别, 都可以配置 IP, 都归属于网络设备管理模块统一调度. 作为网络设备, tap/tun
也需要相应的驱动程序: 字符设备驱动, 网卡驱动, 其中他们的字符驱动设备文件如下:
tap: /dev/tap0
, tap 是一个二层或者以太网设备, 仅仅处理以太网帧(mac)
tun: /dev/net/tun
, tun 是一个点对点的三层或者网络层设备, 仅仅处理 IP 数据包
tap/tun
常常用于用于隧道通信, 比如 VPN. tap/tun
和网络协议栈的数据传输同物理网卡流程大体类似, 但是有一些区别:
物理网卡: 应用程序 — 网络协议栈(network protocol stack) — eth0 — 物理设备
tap/tun
: 应用程序 — 网络协议栈 — tun0 — 应用程序的用户空间, 类似一个管道
用户程序向/dev/tap0
或者/dev/net/tun
中 send 数据, 内核协议栈从相应接口 recv 数据并通过其他接口转化出去, 反之亦然.
veth-pair veth-pair
网络是一对的虚拟设备接口, veth-pair
不同于tap/tun
, 这是一对设备接口, 一端连接着协议栈, 一端连接着彼此, 故它常常作为桥梁连接着各种虚拟网络, 例如两个 NS 之间, docker 容器之间的连接, 并基于此构建复杂的虚拟网络结构. 下面是 docker 中宿主机和容器之间的veth-pair
网卡的抓包记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 tcpdump -nnt -i veth4465607 ping -c 2 172.17.0.2 ping -I docker0 -c 2 172.17.0.2 listening on veth4465607, link-type EN10MB (Ethernet), capture size 262144 bytes ARP, Request who-has 172.17.0.2 tell 172.17.0.1, length 28 ARP, Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28 IP 172.17.0.1 > 172.17.0.2: ICMP echo request, id 4, seq 1, length 64 IP 172.17.0.2 > 172.17.0.1: ICMP echo reply, id 4, seq 1, length 64
我们可以使用ethtool
和ip addr
或者ip link
简单看下veth-pair
的对应关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ethtool -S veth4465607 NIC statistics: peer_ifindex: 7 8: veth4465607@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link /ether 52:7c:f1:ac:80:70 brd ff:ff:ff:ff:ff:ff link-netnsid 0 ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link /ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
关于多个 namespace 之间通过veth-pair
直接相连, 基于 linux bridge 连接veth-pair
, 基于 ovs 连接veth-pair
等结构这里暂不详细了解.
vlan vlan(virtual LAN)也是一种网络设备(虚拟局域网), 一般指代由路由器分割的网络或者广播域. 广播域, 指的是广播帧(目标 MAC 地址全部为 1)所能传递到的范围, 亦即能够直接通信的范围. 当然, 在虚拟化上 linux 也支持 VLAN 网络虚拟化. 通过在交换机上设置 vlan 网络:
避免了广播域在整个大网络
上的发送和占用带宽(每一个广播到达到所有的终端)
提高了网络传递的效率, 减少了终端的负载压力.
一旦设置了 vlan, 即使是在一台交换机上设置了两个不同的 vlan, 则在没有设置 vlan 间通信的配置之前, 两个 vlan 的终端是无法互相通信的. 如果希望 vlan 间能互相通信, 则需要router
提供中继服务, 即Vlan间路由
, 这个后续在其他文章中再详细介绍.
docker 网络模式 Linux 上默认的 Docker 的网络结构如下图:
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 +------------------------------------------------------------------------+ | Host | | | | +------------------+ +------------------+ | | | | | | | | | | | | | | | Container1 | | Container2 | | | | | | | | | | +--------+ | | +--------+ | | | | | eth0 | | | | eth0 | | | | +----+---+----+----+ +----+----+---+----+ | | ^ 172.17.0.2 ^ 172.17.0.3 | | | | | | | | | | v v | | +---+-----+ +----+----+ | | | | | | | | +----+ vethXX +------------------------------+ vethYY +----+ | | | | | | | | | | | +---------+ docker0 +---------+ | | | | +------------+ | | | +-----------------------+ docker0 +-----------------------+ | | +-----+------+ 172.17.0.1/16 | | | | +--------------------------+ | +-----------------------| eth0 |---------------------+ +--------------------------+ 10.10.10.1
默认情况下, 会新建一个docker0
的网桥, docker0 通过在内部软件虚拟出一个交换机, 该网桥会在 docker 服务器启动的时候自动创建, 其 IP 地址一般默认为172.17.0.1/16
, 该网桥将主机和所有使用该网络的容器放在同一个网络命名空间(物理网络), 实现了主机和容器之间的相互通信. docker 利用特殊的技术虚拟出vethXX
(即 2.3 节的 veth-pair 结构), 之后利用这些接口与容器中的ethXX
来进行通信, 这样宿主机和容器, 容器和容器之间就可以互相通信.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql-slave docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql-master 172.17.0.2 172.17.0.3 ip addr show docker0 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:40:2f:01:44 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:40ff:fe2f:144/64 scope link valid_lft forever preferred_lft forever docker network ls docker inspect -f '{{range.IPAM.Config}}{{.Subnet}}{{end}}' bridge 172.17.0.0/16
注意, mac 和 windows 上的 docker 和宿主机之间还封装了一个虚拟机, 所以后两者的网络结构有一些不同. 一般而言, docker 有四种网络模式:
bridge: 默认模式, 独立的 network namespace, 每一个容器有独立的container-ip
, 并见容器连接到docker0
虚拟网桥中, 通过 docker0 和宿主机通信
host: 容器和宿主机共享 Network namespace, 使用宿主机的 IP, 端口
container: 容器和另外一个容器共享 Network namespace, 例如 k8s 中的 pod 就是多个容器共享一个 NS
none: 容器有独立的 NS, 但是 IP 地址等信息未配置, 需要使用者自行设置网络信息
在 docker 服务启动之后, 系统会默认创建三个网络模式:
1 2 3 4 5 6 docker network ls NETWORK ID NAME DRIVER SCOPE 4d2c9e94895b bridge bridge local 87be1e06cac9 host host local eafecb332664 none null local
当然, 除了这三个默认创建的网络模式之外, 用户可以自行创建自定义网络模式, 这是一种非常通用普通的做法, 比如一个 docker-compose 一般都会创建一个唯一的网络用于相关容器的通信.
网桥实战 关于 bridge 的特点和使用场景上面已经提及, 下面是测试例子, 首先创建一个独立的自定义 bridge 网络
1 2 3 4 5 6 docker network create -d bridge localnet docker network inspect localnet docker network rm localnet
在 2.1 节中我们创建了两个容器分别表示主从, 他们使用默认的 docker0 网桥, 现在让我们看下网桥的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 brctl show root@bamboo:~ bridge name bridge id STP enabled interfaces docker0 8000.0242402f0144 no veth02c8df0 vethc2bd32e ip addr show veth02c8df0 10: veth02c8df0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link /ether d6:b0:b8:fc :7f:04 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::d4b0:b8ff:fefc:7f04/64 scope link valid_lft forever preferred_lft forever docker exec -it u1 bash ping 172.17.0.3 docker exec -it u2 bash ping 172.17.0.2
下面我们随便启动两个容器(为了测试, 暂时关闭了上面的 mysql 主从), 在启动之后进入容器之中查看他们的网卡信息确认和上面在宿主机中的输出信息保持一致:
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 29 30 docker run -td --name u1 ubuntu:bifeng bash docker run -td --name u2 ubuntu:bifeng bash docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 382476cbcf79 ubuntu:bifeng "bash" 2 seconds ago Up 1 second u2 8b01d5c7fdca ubuntu:bifeng "bash" 3 seconds ago Up 1 second u1 docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' u1 u2 172.17.0.2 172.17.0.3 ip addr show eth0 15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever ip addr show veth613f48a 16: veth613f48a@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link /ether d6:e9:4e:8b:af:e9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::d4e9:4eff:fe8b:afe9/64 scope link valid_lft forever preferred_lft forever
2.1 节的网络地址结构图可以发现, docker0 网桥为每个容器创建了一个对等虚拟网卡veth pair设备
:
vethNUM: 容器在宿主机网桥中的网络设备名
eth0: 容器中的网络设备名
这两者是一一对应的. 注意, 查看上面宿主机的网络地址输出, 发现其输出 container ip 和 mysql 主从 IP 是一模一样, 这是因为 mysql 主从已经关闭了, 新的 container 的网络 IP 地址分配仍然是从172.17.0.1
开始, 这也是为什么自定义 bridge 或者其他模式提出的意义, 在同一个网络 NS 中, 不同的容器不是使用网络 IP 而是使用网络alias
来进行相互通信, 当然, 对外暴露的服务仍然需要分配公网 IP 地址.
host 模式 注意, macOS 中docker for mac
不支持该模式.在该模式下, 容器将不会拥有自己的网络 namespace, 根据第二节的介绍我们了解到 namespace 是 linux 实现网络虚拟化的关键, 但是在 host 模式下, 容器和宿主机共有一个 NS, 容器不再拥有自己的网卡,IP 信息, 但是除了网络之外的其他资源仍然拥有自己的 NS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ┌──────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ container1 │ │ container2 │ │ │ │ │ │ │ │ │ └──────┬──────┘ └───────┬─────┘ │ │ 80 │ 443 3306 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───────────────┬───────────────┘ │ │ │ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ │ │ └────────────────┤ eth0 ├──────────────┘ │ │ └──────────────────┘ 80 443 3306
none 和 container container 模式下新创建的容器和已经存在的一个容器共享一个 Network Namespace, 新创建的容器不会拥有自己的网卡, IP 地址信息, 但是除了网络之外的其他资源仍然拥有自己的 NS.
none 模式下类似 bridge 模式, 但像 bridge 的初始化未赋值, 此时各个 NS 之中没有网卡, IP 地址信息.
参考: