Docker网络原理详解02
Posted 我的紫霞辣辣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker网络原理详解02相关的知识,希望对你有一定的参考价值。
Docker网络网络基础
Docker 使用到的与 Linux 网络有关的技术分别有:网络名称空间、Veth、Iptables、网桥、路由
网络名称空间
为了支持网络协议栈的多个实例,Linux 在网络协议栈中引入了网络名称空间(Network Namespace), 这些独立的协议栈被隔离到不同的命名空间中。处于不同的命名空间的网络协议栈是完全隔离的,彼此之间无法进行网络通信,就好像两个"平行宇宙"。 通过这种对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境, 而 Docker 正是利用这种网络名称空间的特性,实现了不同容器之间的网络隔离。 在 Linux 的网络命名空间内可以有自己独立的 Iptables 来转发、NAT及 IP 包过滤等功能。 Linux 的网络协议栈是十分复杂的,为了支持独立的协议栈,相关的这些全局变量都必须修改为协议栈私有。 最好的办法就是让这些全局变量成为一个 Net Namespace 变量的成员,然后为了协议栈的函数调用加入一个Namespace 参数。 这就是 Linux 网络名称空间的核心。所以的网络设备都只能属于一个网络名称空间。 当然,通常的物理网络设备只能关联到 root 这个命名空间中。 虚拟网络设备则可以被创建并关联到一个给定的命名空间中,而且可以在这些名称空间之间移动。
自定义Veth-pair
Veth 设备
引入 Veth 设备对是为了在不同的网络名称空间之间进行通信,利用它可以直接将两个网络名称空间链接起来。由于要连接的两个网络命名空间,所以 Veth 设备是成对出现的,很像一对以太网卡,并且中间有一根直连的网线。既然是一对网卡,那么我们将其中一端称为另一端的 peer。在 Veth 设备的一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作。
创建一个命名空间
[root@docker01 ~]# ip netns add test01
[root@docker01 ~]# ip netns add test02
[root@docker01 ~]# ip netns list
test02
test01
创建 Veth 设备对
# 创建 Veth 设备对
[root@docker01 ~]# ip link add name veth01 type veth peer name veth02
# 查看宿主机的网络
[root@docker01 ~]# ip link show
...
5: veth02@veth01: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether da:01:99:1f:69:14 brd ff:ff:ff:ff:ff:ff
6: veth01@veth02: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fe:3c:0a:4e:31:81 brd ff:ff:ff:ff:ff:ff
- 生成了两个veth设备,互为对方的peer
将 veth设备对的一个设备 绑定命名空间
# 将veth01设备绑定给test01名称空间内
[root@docker01 ~]# ip link set veth01 netns test01
[root@docker01 ~]# ip link show | grep veth
5: veth02@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
已经查看不到veth01虚拟网卡,当我们进入 test01 命名空间之后,就可以查看veth01虚拟网卡
# 经入test01名称空间内
[root@docker01 ~]# ip netns exec test01 bash
[root@docker01 ~]# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth01@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fe:3c:0a:4e:31:81 brd ff:ff:ff:ff:ff:ff link-netnsid 0
给 Veth设备 分配 IP
- 为 test01 名称空间内的网卡 veth01 设置ip地址
# 将ip地址 172.16.0.80/20 绑定给test01名称空间内的虚拟网卡veth01
[root@docker01 ~]# ip netns exec test01 ip addr add 172.16.0.80/20 dev veth01
# 启动虚拟网卡veth01
[root@docker01 ~]# ip netns exec test01 ip link set dev veth01 up
# 查看test01名称空间内的虚拟网卡
[root@docker01 ~]# ip netns exec test01 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth01@if5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
link/ether fe:3c:0a:4e:31:81 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.0.80/20 scope global veth01
valid_lft forever preferred_lft forever
[root@docker01 ~]# exit
exit
- 为对端主机的网卡 veth02 设置ip地址
# 将ip地址 172.16.0.890/20 绑定给主机的虚拟网卡veth02
[root@docker01 ~]# ip addr add 172.16.0.90/20 dev veth02
# 重启主机veth02的网卡
[root@docker01 ~]# ip link set dev veth02 down
[root@docker01 ~]# ip link set dev veth02 up
- 测试主机的虚拟网卡veth02连接test01名称空间内的虚拟网卡veth01,连接成功。
[root@docker01 ~]# ping 172.16.0.80
PING 172.16.0.80 (172.16.0.80) 56(84) bytes of data.
64 bytes from 172.16.0.80: icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from 172.16.0.80: icmp_seq=2 ttl=64 time=0.072 ms
结论 :
网络连接设备比较小众的情况下,使用Veth pair进行直接连接(一对一进行连接,效率更高);
网络连接设备比较多的情况下,使用网桥进行连接(多了一个类似于路由器一样的设备进行转发)。
Iptables/Netfilter
Linux协议栈非常高效且复杂。
如果我们想要在数据处理过程中对关心的数据进行一些操作,则需要Linux提供一套相应的机制帮助用户实现自定义的数据包处理过程。在Linux网络协议栈有一组网络回调函数挂接点,通过这些挂接点函数挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包一些操作,例如过滤、修改、丢弃等。整个挂接点技术叫做Iptables和Netfilter。
Netfilter负责在内核中执行各种各样的挂接规则,运行在内核模式中。
而Iptables是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表。
通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。
总结
设备 | 作用总结 |
---|---|
network namespace | 主要提供了关于网络资源的隔离,包括网络设备、IPv4 和 IPv6 协议栈、IP 路由表、防火墙、/proc/net 目录、/sys/class/net 目录、端口(socket)等。 |
linux Bridge | 功能相当于物理交换机,为连在其上的设备(容器)转发数据帧。如 docker0 网桥。 |
iptables | 主要为容器提供 NAT 以及容器网络安全 |
veth pair | 两个虚拟网卡组成的数据通道。在 Docker 中,用于连接 Docker 容器和 Linux Bridge。一端在容器中作为 eth0 网卡,另一端在 Linux Bridge 中作为网桥的一个端口。 |
Docker 四种网络模式
Docker网络模式
Docker 使用 Linux 桥接的方式,在宿主机虚拟一个 Docker 容器网桥(docker0)。 Docker 启动一个容器时会 根据 Docker 网桥的网段分配给容器一个 IP 地址,称为 Container-IP。 同时 Docker 网桥是每个容器的默认网关。 因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 直接通信。 Docker 网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的。 这也意味着外 部网络无法通过直接 Container-IP 访问到容器。 如果容器希望外部访问能够访问到,可以通过映射容器端口到 宿主主机(端口映射), 即 docker run 创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机 IP]:[容器端口]访问容器。
host 模式
指定容器的网络模式为host模式:docker run -d --network=host nginx
# 启动一个容器,指定模式为host模式
[root@docker01 ~]# docker run -d --network=host nginx
5afb9b8df8c311609677aaf7f6bff11467a34bb5b804f8eb9498232ee018d756
浏览器输入主机的ip地址: http://192.168.15.80/
可以正常访问到nginx
结论:容器和宿主机共享Network namespace,公用宿主机网卡。
Containe 模式
指定容器的网络模式为host模式(容器与容器之间的端口号不能重复!!!):docker run -d --network="container:容器id" nginx
容器A:
# 启动一个centos容器,指定网络模式为host模式,公用宿主机的网卡
[root@docker01 ~]# docker run -it --network=host centos
# 查看网卡信息
[root@docker01 /]# ip a
...
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group defaul
link/ether 02:42:aa:7b:f5:2d 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:aaff:fe7b:f52d/64 scope link
valid_lft forever preferred_lft forever
容器B:
# 再次启动一个centos容器,指定网络模式为container,id指定为第一个容器A的centos网卡
[root@docker01 ~]# docker run -it --network="container:82e5e0944002" centos
# 网卡信息与容器A一致,说明跟容器A公用一个网卡(都是使用的是宿主机的网卡)
[root@docker01 /]# ip a
...
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group defaul
link/ether 02:42:aa:7b:f5:2d 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:aaff:fe7b:f52d/64 scope link
valid_lft forever preferred_lft forever
# 再打开一个终端,查看正在运行的容器
[root@docker01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fc553578de82 centos "/bin/bash" 3 minutes ago Up 3 minutes happy_euclid
82e5e0944002 centos "/bin/bash" 6 minutes ago Up 6 minutes busy_hoover
注意:使用container模式,容器与容器之间的端口号不能重复。
none 模式
指定容器的网络模式为none模式:docker run -it --network=none nginx
# 启动一个nginx容器,指定网络模式为none模式
[root@docker01 ~]# docker run -it --network=none centos
# 查看容器ip信息,容器拥有自己独立的名称空间,但是默认的ip地址只有本地回环地址。
[root@ee42ffce97ef /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
bridge模式
当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,dokcer创建容器默认的模式为bridge模式。
bridge模式相关命令
# bridge模式常用命令
[root@docker01 ~]# docker network --help
Usage: docker network COMMAND
Manage networks
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
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
-- 创建一个网桥: create
# --driver 指定网络模式
# --subnet 指定子网地址
# --gateway 指定网关地址
[root@docker01 ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
-- 查看网桥列表: ls
[root@docker01 ~]# docker network ls
-- 查看网桥基本信息: inspect
[root@docker01 ~]# docker network inspect mynet
-- 添加一个容器到网桥,网桥默认会给新添加的容器分配同一个网段的ip: connect
# 启动一个nginx容器,默认的网桥是docker0
[root@docker01 ~]# docker run -d nginx
# 启动一个centos容器,并进入容器内,指定为自定义网桥mynet
[root@docker01 ~]# docker run -it network=mynet centos
# 不同的网桥之间默认是无法连接的,在centos容器内是连接不上nginx容器的
[root@8a8088cfa5fd /]# curl 9a475eeeb3e9
curl: (6) Could not resolve host: 9a475eeeb3e9
# 将nginx的容器id添加至mynet网桥
[root@docker01 ~]# docker network connect mynet 9a475eeeb3e9
# 在centos镜像内重新访问连接nginx,测试成功
[root@8a8088cfa5fd /]# curl 9a475eeeb3e9
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
-- 删除网桥内的一个容器: disconnect
[root@docker01 ~]# docker network disconnect mynet 9a475eeeb3e9
[root@8a8088cfa5fd /]# curl 9a475eeeb3e9
curl: (6) Could not resolve host: 9a475eeeb3e9
-- 清空所有当前没有正在使用的网桥: prune (docker默认的网桥是无法被清理的)
# 创建两个网桥
[root@docker01 ~]# docker network create test01
[root@docker01 ~]# docker network create test02
# 查看网桥列表
[root@docker01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f1de61811860 bridge bridge local
e185b787c28e host host local
0a046b8f5a42 mynet bridge local
01c19cbebe06 none null local
91b6a150a909 test01 bridge local
ce6b6e692235 test02 bridge local
# 清理未被使用的网桥
[root@docker01 ~]# docker network prune
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
test01
test02
-- 删除指定的网桥: rm
[root@docker01 ~]# docker network rm test01
以上是关于Docker网络原理详解02的主要内容,如果未能解决你的问题,请参考以下文章
Docker 网络详解(hostcontainernoneBridge网络模式大解析)
Docker 网络详解之使用 --link 实现容器间通过容器名称互联